├── .gitignore ├── manifest.mf ├── test └── org │ └── burstsort4j │ ├── dictcalls.gz │ ├── dictwords.gz │ ├── benchmark │ └── DataGeneratorTest.java │ ├── CombsortTest.java │ ├── IntrosortTest.java │ ├── QuicksortTest.java │ ├── HybridCombsortTest.java │ ├── MultikeyQuicksortTest.java │ ├── DualPivotQuicksortTest.java │ ├── GnomesortTest.java │ ├── SelectionsortTest.java │ ├── Tests.java │ ├── ShellsortTest.java │ ├── HeapsortTest.java │ ├── InsertionsortTest.java │ ├── BinaryInsertionsortTest.java │ ├── FunnelsortTest.java │ ├── BurstsortTest.java │ └── RedesignedBurstsortTest.java ├── src └── org │ └── burstsort4j │ ├── benchmark │ ├── BenchmarkRunnable.java │ ├── BenchmarkResult.java │ ├── DataSize.java │ ├── BenchmarkData.java │ ├── SortRunner.java │ ├── MacroBenchmark.java │ ├── MicroBenchmark.java │ ├── DataGenerator.java │ └── HugeBenchmark.java │ ├── package.html │ ├── Gnomesort.java │ ├── Shellsort.java │ ├── Combsort.java │ ├── HybridCombsort.java │ ├── Selectionsort.java │ ├── Insertionsort.java │ ├── Heapsort.java │ ├── Quicksort.java │ ├── Main.java │ ├── BinaryInsertionsort.java │ ├── DualPivotQuicksort.java │ ├── MultikeyQuicksort.java │ └── Introsort.java ├── nbproject ├── project.xml ├── genfiles.properties ├── project.properties └── profiler-build-impl.xml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ 2 | /build/ 3 | /dist/ -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /test/org/burstsort4j/dictcalls.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nlfiedler/burstsort4j/HEAD/test/org/burstsort4j/dictcalls.gz -------------------------------------------------------------------------------- /test/org/burstsort4j/dictwords.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nlfiedler/burstsort4j/HEAD/test/org/burstsort4j/dictwords.gz -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/BenchmarkRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | /** 9 | * That which is run in a single benchmark test. 10 | * 11 | * @author Nathan Fiedler 12 | */ 13 | public interface BenchmarkRunnable { 14 | 15 | /** 16 | * Run the benchmark the number of times specified in {@code b}. 17 | * 18 | * @param b provides the number of iterations. 19 | */ 20 | void run(BenchmarkData b); 21 | } 22 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | burstsort4j 7 | 1.6.5 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=8016d362 2 | build.xml.script.CRC32=aa081365 3 | build.xml.stylesheet.CRC32=be360661 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=8016d362 7 | nbproject/build-impl.xml.script.CRC32=e3234127 8 | nbproject/build-impl.xml.stylesheet.CRC32=5a01deb7@1.68.1.46 9 | nbproject/profiler-build-impl.xml.data.CRC32=8016d362 10 | nbproject/profiler-build-impl.xml.script.CRC32=abda56ed 11 | nbproject/profiler-build-impl.xml.stylesheet.CRC32=42cb6bcf 12 | -------------------------------------------------------------------------------- /src/org/burstsort4j/package.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | keeps HTML tidy happy 11 | 12 | 13 | 14 |

The burstsort4j package contains the generally interesting 15 | classes, namely Burstsort, which implements the original 16 | Burstsort algorithm. The package also contains a multikey quicksort 17 | implementation, as used by burstsort, as well as a basic quicksort 18 | and insertionsort.

19 | 20 | 21 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/BenchmarkResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | /** 9 | * Data regarding the run of a benchmark, including the number of 10 | * iterations and the number of nanoseconds per iteration. 11 | * 12 | * @author Nathan Fiedler 13 | */ 14 | public class BenchmarkResult { 15 | 16 | private final int count; 17 | private final long ns; 18 | 19 | BenchmarkResult(int count, long ns) { 20 | this.count = count; 21 | this.ns = ns; 22 | } 23 | 24 | public int count() { 25 | return count; 26 | } 27 | 28 | public long nsPerOp() { 29 | if (count <= 0) { 30 | return 0; 31 | } 32 | return ns / count; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/org/burstsort4j/benchmark/DataGeneratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import org.burstsort4j.Quicksort; 9 | import org.junit.Test; 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Unit tests for the DataGenerator class. 14 | * 15 | * @author Nathan Fiedler 16 | */ 17 | public class DataGeneratorTest { 18 | 19 | @Test 20 | public void testMedianOf3Killer() { 21 | DataGenerator generator = DataGenerator.MEDIAN_OF_3_KILLER; 22 | String[] results = generator.generate(DataSize.N_20); 23 | String[] sorted = new String[results.length]; 24 | System.arraycopy(results, 0, sorted, 0, results.length); 25 | Quicksort.sort(sorted); 26 | int[] order = new int[]{0, 10, 2, 12, 4, 14, 6, 16, 8, 18, 27 | 1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; 28 | assertEquals(order.length, results.length); 29 | for (int ii = 0; ii < order.length; ii++) { 30 | assertEquals(sorted[order[ii]], results[ii]); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Gnomesort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Implementation of Gnome sort based on pseudocode on Wikipedia. 10 | * 11 | * @author Nathan Fiedler 12 | */ 13 | public class Gnomesort { 14 | 15 | private Gnomesort() { 16 | } 17 | 18 | /** 19 | * Sort the input array using the Gnome sort algorithm. 20 | * O(n^2) running time. 21 | * 22 | * @param type of comparable to be sorted. 23 | * @param input array of comparable objects to be sorted. 24 | */ 25 | public static > void sort(T[] input) { 26 | if (input == null || input.length < 2) { 27 | return; 28 | } 29 | int i = 1; 30 | int j = 2; 31 | while (i < input.length) { 32 | if (input[i - 1].compareTo(input[i]) < 1) { 33 | i = j; 34 | j++; 35 | } else { 36 | // swap a[i-1] and a[i] 37 | T t = input[i - 1]; 38 | input[i - 1] = input[i]; 39 | input[i] = t; 40 | i--; 41 | if (i == 0) { 42 | i = j; 43 | j++; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Shellsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Shell sort implementation based on pseudocode from Wikipedia. 10 | * 11 | * @author Nathan Fiedler 12 | */ 13 | public class Shellsort { 14 | 15 | private Shellsort() { 16 | } 17 | 18 | /** 19 | * Sort the input array using the shell sort algorithm with the gap 20 | * sequence suggested by Gonnet and Baeza-Yates. 21 | * 22 | * @param type of comparable to be sorted. 23 | * @param input array of comparable objects to be sorted. 24 | */ 25 | public static > void sort(T[] input) { 26 | if (input == null || input.length < 2) { 27 | return; 28 | } 29 | 30 | int inc = input.length / 2; 31 | while (inc > 0) { 32 | for (int ii = inc; ii < input.length; ii++) { 33 | T temp = input[ii]; 34 | int jj = ii; 35 | while (jj >= inc && input[jj - inc].compareTo(temp) > 0) { 36 | input[jj] = input[jj - inc]; 37 | jj -= inc; 38 | } 39 | input[jj] = temp; 40 | } 41 | // Another way of dividing by 2.2 to get an integer. 42 | inc = inc == 2 ? 1 : inc * 5 / 11; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Nathan Fiedler. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Nathan Fiedler nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Combsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Implementation of comb sort based on pseudo-code on Wikipedia, 10 | * in particular the Combsort11 algorithm. 11 | * 12 | * @author Nathan Fiedler 13 | */ 14 | public class Combsort { 15 | 16 | private Combsort() { 17 | } 18 | 19 | /** 20 | * Sort the input array using the Combsort11 algorithm. 21 | * Purportedly O(n*logn) running time. 22 | * 23 | * @param type of comparable to be sorted. 24 | * @param input array of comparable objects to be sorted. 25 | */ 26 | public static > void sort(T[] input) { 27 | if (input == null || input.length < 2) { 28 | return; 29 | } 30 | 31 | int gap = input.length; //initialize gap size 32 | boolean swapped = true; 33 | 34 | while (gap > 1 || swapped) { 35 | // Update the gap value for the next comb. 36 | if (gap > 1) { 37 | gap = (gap * 10) / 13; 38 | if (gap == 10 || gap == 9) { 39 | gap = 11; 40 | } 41 | } 42 | 43 | swapped = false; 44 | 45 | // a single "comb" over the input list 46 | for (int i = 0; i + gap < input.length; i++) { 47 | int j = i + gap; 48 | if (input[i].compareTo(input[j]) > 0) { 49 | T tmp = input[i]; 50 | input[i] = input[j]; 51 | input[j] = tmp; 52 | // Signal that the list is not guaranteed sorted. 53 | swapped = true; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/org/burstsort4j/HybridCombsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * An implementation of comb sort that delegates to insertion sort when 10 | * the gap value has dropped below a certain threshold. This variation 11 | * was proposed by David B. Ring of Palo Alto and demonstrated to be 12 | * 10 to 15 percent faster than traditional comb sort. This particular 13 | * implementation uses the Combsort11 variation for determining the 14 | * gap values. 15 | * 16 | * @author Nathan Fiedler 17 | */ 18 | public class HybridCombsort { 19 | 20 | private HybridCombsort() { 21 | } 22 | 23 | /** 24 | * Sort the input array using a hybrid of Combsort11 and Insertion sort. 25 | * 26 | * @param type of comparable to be sorted. 27 | * @param input array of comparable objects to be sorted. 28 | */ 29 | public static > void sort(T[] input) { 30 | if (input == null || input.length < 2) { 31 | return; 32 | } 33 | 34 | int gap = input.length; 35 | while (gap > 8) { 36 | gap = (10 * gap) / 13; 37 | if (gap == 10 || gap == 9) { 38 | gap = 11; 39 | } 40 | for (int i = 0; i + gap < input.length; i++) { 41 | int j = i + gap; 42 | if (input[i].compareTo(input[j]) > 0) { 43 | T tmp = input[i]; 44 | input[i] = input[j]; 45 | input[j] = tmp; 46 | } 47 | } 48 | } 49 | // At this point the input is nearly sorted, a case for which 50 | // insertion sort performs very well. 51 | Insertionsort.sort(input); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Burstsort ## 2 | 3 | Burstsort4j contains an implementation of both the original 4 | [Burstsort](http://en.wikipedia.org/wiki/Burstsort), which is quite fast 5 | but not particularly memory efficient, and the engineered Burstsort, which 6 | is extremely memory efficient and close to the speed of the original in 7 | most cases. Burstsort typically outperforms other string sorting algorithms 8 | for most data sets. 9 | 10 | Both single-threaded and multi-threaded implementations are available, 11 | where the multi-threaded method will utilize all available processor cores 12 | using a simple thread pool executor. In this mode of operation, the buckets 13 | to be sorted are assigned to jobs and sorted in parallel. 14 | 15 | ## Funnelsort ## 16 | 17 | In addition to Burstsort there is an implementation of the Funnelsort 18 | algorithm for sorting strings, which is faster than most string sorts but 19 | not as fast as Burstsort. Funnelsort uses string comparison while Burstsort 20 | is a form of radix sort and hence sorts only lexicographically by 21 | individual characters. The specific implementation of Funnelsort is modeled 22 | after the [Lazy Funnelsort](http://portal.acm.org/citation.cfm?id=1227161.1227164) 23 | described by Brodal, Fagerberg, and Vinther. At the heart of the algorithm 24 | is an [insertion d-way merger](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.63.3896) 25 | as described by Brodal and Moruz. Like Burstsort, the Funnelsort algorithm 26 | is cache- oblivious and thus typically performs well compared to algorithms 27 | that assume a unit-cost for RAM access (e.g. Quicksort). 28 | 29 | ## Multikey Quicksort ## 30 | 31 | Burstsort4j contains a Java implementation of the multikey quicksort 32 | algorithm. It takes a median of three approach to find the pivot, and 33 | delegates to insertion sort for small sublists. This sort is what Burstsort 34 | delegates to for sorting the buckets that hang from the trie structure. 35 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Selectionsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Basic selection sort implementation based on pseudocode on Wikipedia. 10 | * 11 | * @author Nathan Fiedler 12 | */ 13 | public class Selectionsort { 14 | 15 | private Selectionsort() { 16 | } 17 | 18 | /** 19 | * Sort the input array using the selection sort algorithm. 20 | * O(n^2) running time. 21 | * 22 | * @param type of comparable to be sorted. 23 | * @param input array of comparable objects to be sorted. 24 | */ 25 | public static > void sort(T[] input) { 26 | if (input != null && input.length > 1) { 27 | sort(input, 0, input.length - 1); 28 | } 29 | } 30 | 31 | /** 32 | * Sort the input array using the selection sort algorithm. 33 | * O(n^2) running time. 34 | * 35 | * @param type of comparable to be sorted. 36 | * @param input array of comparable objects to be sorted. 37 | * @param low low end of range to sort. 38 | * @param high high end of range to sort (inclusive). 39 | */ 40 | public static > void sort(T[] input, 41 | int low, int high) { 42 | if (input == null || input.length < 2 || high <= low) { 43 | return; 44 | } 45 | 46 | for (int ii = low; ii < high; ii++) { 47 | int min = ii; 48 | for (int jj = ii + 1; jj < input.length; jj++) { 49 | if (input[jj].compareTo(input[min]) < 0) { 50 | min = jj; 51 | } 52 | } 53 | if (ii != min) { 54 | T temp = input[ii]; 55 | input[ii] = input[min]; 56 | input[min] = temp; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/DataSize.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | /** 9 | * Size of the data sets used in testing sort performance. 10 | * For the median-of-three killer generator to work, the 11 | * sizes must be divisible by four. 12 | * 13 | * @author Nathan Fiedler 14 | */ 15 | public enum DataSize { 16 | 17 | N_12 { 18 | 19 | @Override 20 | public int getValue() { 21 | return 12; 22 | } 23 | }, 24 | N_20 { 25 | 26 | @Override 27 | public int getValue() { 28 | return 20; 29 | } 30 | }, 31 | N_52 { 32 | 33 | @Override 34 | public int getValue() { 35 | return 52; 36 | } 37 | }, 38 | N_100 { 39 | 40 | @Override 41 | public int getValue() { 42 | return 100; 43 | } 44 | }, 45 | N_400 { 46 | 47 | @Override 48 | public int getValue() { 49 | return 400; 50 | } 51 | }, 52 | N_800 { 53 | 54 | @Override 55 | public int getValue() { 56 | return 800; 57 | } 58 | }, 59 | N_1000 { 60 | 61 | @Override 62 | public int getValue() { 63 | return 1000; 64 | } 65 | }, 66 | N_4000 { 67 | 68 | @Override 69 | public int getValue() { 70 | return 4000; 71 | } 72 | }, 73 | N_16000 { 74 | 75 | @Override 76 | public int getValue() { 77 | return 16000; 78 | } 79 | }, 80 | N_64000 { 81 | 82 | @Override 83 | public int getValue() { 84 | return 64000; 85 | } 86 | }, 87 | N_256000 { 88 | 89 | @Override 90 | public int getValue() { 91 | return 256000; 92 | } 93 | }, 94 | N_512000 { 95 | 96 | @Override 97 | public int getValue() { 98 | return 512000; 99 | } 100 | }, 101 | N_1024000 { 102 | 103 | @Override 104 | public int getValue() { 105 | return 1024000; 106 | } 107 | }, 108 | N_3000000 { 109 | 110 | @Override 111 | public int getValue() { 112 | return 3000000; 113 | } 114 | }; 115 | 116 | /** 117 | * Returns the quantity for this data size. 118 | * 119 | * @return quantity. 120 | */ 121 | public abstract int getValue(); 122 | }; 123 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | application.desc=Java implementations of cache-oblivious sorting algorithms. 6 | application.homepage=https://github.com/nlfiedler/burstsort4j/ 7 | application.title=burstsort4j 8 | application.vendor=Nathan Fiedler 9 | auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml 10 | build.classes.dir=${build.dir}/classes 11 | build.classes.excludes=**/*.java,**/*.form 12 | # This directory is removed when the project is cleaned: 13 | build.dir=build 14 | build.generated.dir=${build.dir}/generated 15 | build.generated.sources.dir=${build.dir}/generated-sources 16 | # Only compile against the classpath explicitly listed here: 17 | build.sysclasspath=ignore 18 | build.test.classes.dir=${build.dir}/test/classes 19 | build.test.results.dir=${build.dir}/test/results 20 | debug.classpath=\ 21 | ${run.classpath} 22 | debug.test.classpath=\ 23 | ${run.test.classpath} 24 | # This directory is removed when the project is cleaned: 25 | dist.dir=dist 26 | dist.jar=${dist.dir}/burstsort4j.jar 27 | dist.javadoc.dir=${dist.dir}/javadoc 28 | endorsed.classpath= 29 | excludes= 30 | includes=** 31 | jar.archive.disabled=${jnlp.enabled} 32 | jar.compress=false 33 | jar.index=${jnlp.enabled} 34 | javac.classpath= 35 | # Space-separated list of extra javac options 36 | javac.compilerargs=-Xlint 37 | javac.deprecation=true 38 | javac.processorpath=\ 39 | ${javac.classpath} 40 | javac.source=1.7 41 | javac.target=1.7 42 | javac.test.classpath=\ 43 | ${javac.classpath}:\ 44 | ${build.classes.dir}:\ 45 | ${libs.junit_4.classpath} 46 | javadoc.additionalparam= 47 | javadoc.author=false 48 | javadoc.encoding=${source.encoding} 49 | javadoc.noindex=false 50 | javadoc.nonavbar=false 51 | javadoc.notree=false 52 | javadoc.private=false 53 | javadoc.splitindex=true 54 | javadoc.use=true 55 | javadoc.version=false 56 | javadoc.windowtitle=Burstsort for Java 57 | jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" 58 | jnlp.codebase.type=local 59 | jnlp.codebase.url=file:/Users/nfiedler/projects/burstsort4j/trunk/dist/ 60 | jnlp.descriptor=application 61 | jnlp.enabled=false 62 | jnlp.mixed.code=default 63 | jnlp.offline-allowed=false 64 | jnlp.signed=false 65 | jnlp.signing= 66 | jnlp.signing.alias= 67 | jnlp.signing.keystore= 68 | main.class=org.burstsort4j.benchmark.MicroBenchmark 69 | # Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed 70 | manifest.custom.codebase= 71 | # Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) 72 | manifest.custom.permissions= 73 | manifest.file=manifest.mf 74 | meta.inf.dir=${src.dir}/META-INF 75 | mkdist.disabled=false 76 | platform.active=default_platform 77 | project.license=default 78 | run.classpath=\ 79 | ${javac.classpath}:\ 80 | ${build.classes.dir} 81 | run.jvmargs=-server -Xmx2048m 82 | run.test.classpath=\ 83 | ${javac.test.classpath}:\ 84 | ${build.test.classes.dir} 85 | source.encoding=UTF-8 86 | src.dir=src 87 | test.src.dir=test 88 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/BenchmarkData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | /** 9 | * The data regarding a benchmark, including the elapsed time and 10 | * a timer for tracking the current run. 11 | * 12 | * @author Nathan Fiedler 13 | */ 14 | public class BenchmarkData { 15 | 16 | /** Number of nanoseconds in one second. */ 17 | private static final int ONE_BILLION = 1000000000; 18 | private final BenchmarkRunnable func; 19 | private int count; 20 | private long start; 21 | private long ns; 22 | 23 | public BenchmarkData(BenchmarkRunnable r) { 24 | func = r; 25 | } 26 | 27 | public int count() { 28 | return count; 29 | } 30 | 31 | public void startTimer() { 32 | start = System.nanoTime(); 33 | } 34 | 35 | public void stopTimer() { 36 | if (start > 0) { 37 | ns += System.nanoTime() - start; 38 | } 39 | start = 0; 40 | } 41 | 42 | public void resetTimer() { 43 | start = 0; 44 | ns = 0; 45 | } 46 | 47 | public long nsPerOp() { 48 | if (count <= 0) { 49 | return 0; 50 | } 51 | return ns / count; 52 | } 53 | 54 | private void runN(int n) { 55 | count = n; 56 | resetTimer(); 57 | startTimer(); 58 | func.run(this); 59 | stopTimer(); 60 | } 61 | 62 | private int roundDown10(int n) { 63 | int tens = 0; 64 | while (n > 10) { 65 | n /= 10; 66 | tens++; 67 | } 68 | int result = 1; 69 | for (int i = 0; i < tens; i++) { 70 | result *= 10; 71 | } 72 | return result; 73 | } 74 | 75 | private int roundUp(int n) { 76 | int base = roundDown10(n); 77 | if (n < (2 * base)) { 78 | return 2 * base; 79 | } 80 | if (n < (5 * base)) { 81 | return 5 * base; 82 | } 83 | return 10 * base; 84 | } 85 | 86 | /** 87 | * Run the benchmark function a sufficient number of times to 88 | * get a timing of at least one second. 89 | * 90 | * @return the results of the benchmark run. 91 | */ 92 | public BenchmarkResult run() { 93 | // This code is a translation of that found in the Go testing package. 94 | // Run the benchmark for a single iteration in case it's expensive. 95 | int n = 1; 96 | runN(n); 97 | // Run the benchmark for at least a second. 98 | while (ns < ONE_BILLION && n < ONE_BILLION) { 99 | int last = n; 100 | // Predict iterations/sec. 101 | if (nsPerOp() == 0) { 102 | n = ONE_BILLION; 103 | } else { 104 | n = ONE_BILLION / (int) nsPerOp(); 105 | } 106 | // Run more iterations than we think we'll need for a second (1.5x). 107 | // Don't grow too fast in case we had timing errors previously. 108 | // Be sure to run at least one more than last time. 109 | n = Math.max(Math.min(n + n / 2, 100 * last), last + 1); 110 | // Round up to something easy to read. 111 | n = roundUp(n); 112 | runN(n); 113 | } 114 | return new BenchmarkResult(count, ns); 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Insertionsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Simple implementation of insertion sort algorithm. Used by the 10 | * multikey quicksort implementation for small subarrays. 11 | * 12 | * @author Nathan Fiedler 13 | */ 14 | public class Insertionsort { 15 | 16 | private Insertionsort() { 17 | } 18 | 19 | /** 20 | * Sort the array of comparables. Uses a simple insertion sort 21 | * algorithm, so expect O(n^2) running time. 22 | * 23 | * @param type of comparable to be sorted. 24 | * @param arr comparables to be sorted. 25 | */ 26 | public static > void sort(T[] arr) { 27 | if (arr != null) { 28 | sort(arr, 0, arr.length - 1); 29 | } 30 | } 31 | 32 | /** 33 | * Sort the array of comparables within the given range of elements. 34 | * Uses a simple insertion sort algorithm, so expect O(n^2) running 35 | * time. 36 | * 37 | * @param type of comparable to be sorted. 38 | * @param arr comparables to be sorted. 39 | * @param low low end of range to sort (inclusive). 40 | * @param high high end of range to sort (inclusive). 41 | */ 42 | public static > void sort(T[] arr, int low, int high) { 43 | if (arr == null || arr.length < 2 || low < 0 || high <= low) { 44 | return; 45 | } 46 | 47 | for (int i = low + 1; i <= high; i++) { 48 | T pivot = arr[i]; 49 | int j = i; 50 | while (j > low && pivot.compareTo(arr[j - 1]) < 0) { 51 | arr[j] = arr[j - 1]; 52 | j--; 53 | } 54 | arr[j] = pivot; 55 | } 56 | } 57 | 58 | /** 59 | * Sort the strings in the array using an insertion sort, but only 60 | * consider the characters in the strings starting from the given 61 | * offset depth. That is, the method will ignore all characters 62 | * appearing before the depth character. 63 | * 64 | * @param strings array of strings to sort. 65 | * @param low low offset into the array (inclusive). 66 | * @param high high offset into the array (exclusive). 67 | * @param depth offset of first character in each string to compare. 68 | */ 69 | public static void sort(CharSequence[] strings, int low, int high, int depth) { 70 | if (strings == null || low < 0 || high <= low || depth < 0) { 71 | return; 72 | } 73 | for (int i = low + 1; i < high; i++) { 74 | for (int j = i; j > low; j--) { 75 | int idx = depth; 76 | char s = idx < strings[j - 1].length() ? strings[j - 1].charAt(idx) : 0; 77 | char t = idx < strings[j].length() ? strings[j].charAt(idx) : 0; 78 | while (s == t && idx < strings[j - 1].length()) { 79 | idx++; 80 | s = idx < strings[j - 1].length() ? strings[j - 1].charAt(idx) : 0; 81 | t = idx < strings[j].length() ? strings[j].charAt(idx) : 0; 82 | } 83 | if (s <= t) { 84 | break; 85 | } 86 | CharSequence tmp = strings[j]; 87 | strings[j] = strings[j - 1]; 88 | strings[j - 1] = tmp; 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Heapsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Binary heap sort implementation based on pseudocode from Wikipedia. 10 | * 11 | * @author Nathan Fiedler 12 | */ 13 | public class Heapsort { 14 | 15 | private Heapsort() { 16 | } 17 | 18 | /** 19 | * Sort the input array using the heap sort algorithm. 20 | * O(n*logn) running time with constant extra space. 21 | * 22 | * @param type of comparable to be sorted. 23 | * @param input array of comparable objects to be sorted. 24 | */ 25 | public static > void sort(T[] input) { 26 | if (input == null || input.length < 2) { 27 | return; 28 | } 29 | 30 | // start is assigned the index in a of the last parent node 31 | for (int start = (input.length - 2) / 2; start >= 0; start--) { 32 | // sift down the node at index start to the proper place such 33 | // that all nodes below the start index are in heap order 34 | int root = start; 35 | // While the root has at least one child 36 | while (root * 2 + 1 < input.length) { 37 | // root*2+1 points to the left child 38 | int child = root * 2 + 1; 39 | // If the child has a sibling and the child's value 40 | // is less than its sibling's... 41 | if ((child + 1 < input.length) && 42 | input[child].compareTo(input[child + 1]) < 0) { 43 | // ... then point to the right child instead 44 | child++; 45 | } 46 | // out of max-heap order 47 | if (input[root].compareTo(input[child]) < 0) { 48 | T temp = input[root]; 49 | input[root] = input[child]; 50 | input[child] = temp; 51 | // repeat to continue sifting down the child now 52 | root = child; 53 | } else { 54 | break; 55 | } 56 | } 57 | } 58 | // after sifting down the root all nodes/elements are in heap order 59 | 60 | for (int end = input.length - 1; end > 0; end--) { 61 | // swap the root (maximum value) of the heap with the last 62 | // element of the heap 63 | T temp = input[end]; 64 | input[end] = input[0]; 65 | input[0] = temp; 66 | // put the heap back in max-heap order 67 | int root = 0; 68 | // While the root has at least one child 69 | while (root * 2 + 1 < end) { 70 | // root*2+1 points to the left child 71 | int child = root * 2 + 1; 72 | // If the child has a sibling and the child's value is 73 | // less than its sibling's... 74 | if ((child + 1 < end) && 75 | input[child].compareTo(input[child + 1]) < 0) { 76 | // ... then point to the right child instead 77 | child++; 78 | } 79 | // out of max-heap order 80 | if (input[root].compareTo(input[child]) < 0) { 81 | temp = input[root]; 82 | input[root] = input[child]; 83 | input[child] = temp; 84 | // repeat to continue sifting down the child now 85 | root = child; 86 | } else { 87 | break; 88 | } 89 | } 90 | // end of for loop decreases the size of the heap by one so that 91 | // the previous max value will stay in its proper placement 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Quicksort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * This is the traditional quicksort algorithm. It is comparison-based, 10 | * which for strings, is considerably less efficient than a radix-based 11 | * sort, and in particular, multikey quicksort. 12 | * 13 | *

If you are sorting arrays of primitives, consider using the 14 | * java.util.Arrays class instead, which uses additional 15 | * heuristics to improve performance.

16 | * 17 | * @author Nathan Fiedler 18 | */ 19 | public class Quicksort { 20 | 21 | /** As with GCC std::sort delegate to insertion sort for ranges of 22 | * size below 16. */ 23 | private static final int THRESHOLD = 16; 24 | 25 | /** 26 | * Creates a new instance of Quicksort. 27 | */ 28 | private Quicksort() { 29 | } 30 | 31 | /** 32 | * Sorts the given array of comparable objects using the standard 33 | * quicksort algorithm. This sort is not stable. The objects 34 | * are sorted in place with constant additional memory (not counting 35 | * the stack due to recursion). 36 | * 37 | * @param type of comparable to be sorted. 38 | * @param arr an array of Comparable items to sort. 39 | */ 40 | public static > void sort(T[] arr) { 41 | if (arr != null && arr.length > 1) { 42 | sort(arr, 0, arr.length - 1); 43 | } 44 | } 45 | 46 | /** 47 | * Basic implementation of quicksort. Uses median-of-three partitioning 48 | * and a cutoff at which point insertion sort is used. 49 | * 50 | * @param type of comparable to be sorted. 51 | * @param arr an array of Comparable items. 52 | * @param low the left-most index of the subarray. 53 | * @param high the right-most index of the subarray. 54 | */ 55 | public static > void sort(T[] arr, int low, int high) { 56 | if (low + THRESHOLD > high) { 57 | // Insertion sort for small partitions. 58 | Insertionsort.sort(arr, low, high); 59 | } else { 60 | // Choose a partition element 61 | int middle = (low + high) / 2; 62 | // Order the low, middle, and high elements 63 | if (arr[middle].compareTo(arr[low]) < 0) { 64 | swap(arr, low, middle); 65 | } 66 | if (arr[high].compareTo(arr[low]) < 0) { 67 | swap(arr, low, high); 68 | } 69 | if (arr[high].compareTo(arr[middle]) < 0) { 70 | swap(arr, middle, high); 71 | } 72 | // Place pivot element at the high end in preparation 73 | // for the ensuing swapping of elements. 74 | swap(arr, middle, high - 1); 75 | T pivot = arr[high - 1]; 76 | 77 | // Order the elements such that those below the pivot 78 | // point appear earlier, and those higher than the pivot 79 | // appear later. 80 | int i = low; 81 | int j = high - 1; 82 | while (true) { 83 | while (arr[++i].compareTo(pivot) < 0) { 84 | } 85 | while (pivot.compareTo(arr[--j]) < 0) { 86 | } 87 | if (i >= j) { 88 | break; 89 | } 90 | swap(arr, i, j); 91 | } 92 | 93 | // Restore pivot element to its rightful position. 94 | swap(arr, i, high - 1); 95 | // Sort low partition recursively. 96 | sort(arr, low, i - 1); 97 | // Sort high partition recursively. 98 | sort(arr, i + 1, high); 99 | } 100 | } 101 | 102 | /** 103 | * Method to swap to elements in an array. 104 | * 105 | * @param a an array of objects. 106 | * @param x the index of the first object. 107 | * @param y the index of the second object. 108 | */ 109 | private static void swap(Object[] a, int x, int y) { 110 | Object tmp = a[x]; 111 | a[x] = a[y]; 112 | a[y] = tmp; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /test/org/burstsort4j/CombsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Combsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class CombsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Combsort.sort((String[]) null); 26 | Combsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Combsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Combsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Combsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Combsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Combsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | Combsort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | Combsort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | Combsort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | Combsort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | Combsort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | Combsort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | Combsort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | Combsort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/IntrosortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Introsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class IntrosortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Introsort.sort((String[]) null); 26 | Introsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Introsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Introsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Introsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Introsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Introsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | Introsort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | Introsort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | Introsort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | Introsort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | Introsort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | Introsort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | Introsort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | Introsort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/QuicksortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Quicksort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class QuicksortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Quicksort.sort((String[]) null); 26 | Quicksort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Quicksort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Quicksort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Quicksort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Quicksort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Quicksort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | Quicksort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | Quicksort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | Quicksort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | Quicksort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | Quicksort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | Quicksort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | Quicksort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | Quicksort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/HybridCombsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the HybridCombsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class HybridCombsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | HybridCombsort.sort((String[]) null); 26 | HybridCombsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | HybridCombsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | HybridCombsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | HybridCombsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | HybridCombsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | HybridCombsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | HybridCombsort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | HybridCombsort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | HybridCombsort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | HybridCombsort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | HybridCombsort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | HybridCombsort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | HybridCombsort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | HybridCombsort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/MultikeyQuicksortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit test for the MultikeyQuicksort implementation. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class MultikeyQuicksortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | MultikeyQuicksort.sort((String[]) null); 26 | MultikeyQuicksort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | MultikeyQuicksort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | MultikeyQuicksort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | MultikeyQuicksort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | MultikeyQuicksort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | MultikeyQuicksort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | MultikeyQuicksort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | MultikeyQuicksort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | MultikeyQuicksort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | MultikeyQuicksort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | MultikeyQuicksort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | MultikeyQuicksort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | MultikeyQuicksort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | MultikeyQuicksort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/DualPivotQuicksortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the DualPivotQuicksort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class DualPivotQuicksortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | DualPivotQuicksort.sort((String[]) null); 26 | DualPivotQuicksort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | DualPivotQuicksort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | DualPivotQuicksort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | DualPivotQuicksort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | DualPivotQuicksort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | DualPivotQuicksort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | String[] arr = data.toArray(new String[data.size()]); 53 | DualPivotQuicksort.sort(arr); 54 | assertTrue(Tests.isSorted(arr)); 55 | } catch (IOException ioe) { 56 | fail(ioe.toString()); 57 | } 58 | } 59 | 60 | @Test 61 | public void testSorted() { 62 | try { 63 | List data = Tests.loadData(); 64 | Collections.sort(data); 65 | String[] arr = data.toArray(new String[data.size()]); 66 | DualPivotQuicksort.sort(arr); 67 | assertTrue(Tests.isSorted(arr)); 68 | } catch (IOException ioe) { 69 | fail(ioe.toString()); 70 | } 71 | } 72 | 73 | @Test 74 | public void testReversed() { 75 | try { 76 | List data = Tests.loadData(); 77 | Collections.sort(data); 78 | Collections.reverse(data); 79 | String[] arr = data.toArray(new String[data.size()]); 80 | DualPivotQuicksort.sort(arr); 81 | assertTrue(Tests.isSorted(arr)); 82 | } catch (IOException ioe) { 83 | fail(ioe.toString()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testRepeated() { 89 | String[] arr = new String[10000]; 90 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 91 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 92 | Arrays.fill(arr, STR); 93 | DualPivotQuicksort.sort(arr); 94 | assertTrue(Tests.isRepeated(arr, STR)); 95 | } 96 | 97 | @Test 98 | public void testRepeatedCycle() { 99 | String[] strs = new String[100]; 100 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 101 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 102 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 103 | strs[i] = seed.substring(0, l); 104 | } 105 | List list = new ArrayList(); 106 | for (int c = 10000, i = 0; c > 0; i++, c--) { 107 | list.add(strs[i % strs.length]); 108 | } 109 | String[] arr = list.toArray(new String[list.size()]); 110 | DualPivotQuicksort.sort(arr); 111 | assertTrue(Tests.isSorted(arr)); 112 | } 113 | 114 | @Test 115 | public void testRandom() { 116 | List data = Tests.generateData(10000, 100); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | DualPivotQuicksort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } 121 | 122 | @Test 123 | public void testHamlet() { 124 | try { 125 | List data = Tests.loadData("hamletwords"); 126 | Collections.shuffle(data); 127 | String[] arr = data.toArray(new String[data.size()]); 128 | DualPivotQuicksort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } catch (IOException ioe) { 131 | fail(ioe.toString()); 132 | } 133 | } 134 | 135 | @Test 136 | public void testDictCalls() { 137 | try { 138 | List data = Tests.loadData("dictcalls.gz", true); 139 | String[] arr = data.toArray(new String[data.size()]); 140 | DualPivotQuicksort.sort(arr); 141 | assertTrue(Tests.isSorted(arr)); 142 | } catch (IOException ioe) { 143 | fail(ioe.toString()); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/org/burstsort4j/GnomesortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Gnomesort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class GnomesortTest { 22 | 23 | /** Maximum number of lines to test for any given file, which avoids 24 | * spending far too much time testing insertion sort on data sets 25 | * that it will never actually be called upon to sort in practice. */ 26 | private static final int MAX_LINES = 512; 27 | 28 | @Test 29 | public void testArguments() { 30 | Gnomesort.sort((String[]) null); 31 | Gnomesort.sort(new String[0]); 32 | String[] arr = new String[]{"a"}; 33 | Gnomesort.sort(arr); 34 | arr = new String[]{"b", "a"}; 35 | Gnomesort.sort(arr); 36 | assertTrue(Tests.isSorted(arr)); 37 | arr = new String[]{"c", "b", "a"}; 38 | Gnomesort.sort(arr); 39 | assertTrue(Tests.isSorted(arr)); 40 | // test with all empty input 41 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 42 | Gnomesort.sort(arr); 43 | for (String s : arr) { 44 | assertEquals("", s); 45 | } 46 | // test with peculiar input 47 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 48 | Gnomesort.sort(arr); 49 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 50 | } 51 | 52 | @Test 53 | public void testDictWords() { 54 | try { 55 | List data = Tests.loadData("dictwords", false, MAX_LINES); 56 | Collections.shuffle(data); 57 | String[] arr = data.toArray(new String[data.size()]); 58 | Gnomesort.sort(arr); 59 | assertTrue(Tests.isSorted(arr)); 60 | } catch (IOException ioe) { 61 | fail(ioe.toString()); 62 | } 63 | } 64 | 65 | @Test 66 | public void testSorted() { 67 | try { 68 | List data = Tests.loadData("dictwords", false, MAX_LINES); 69 | Collections.sort(data); 70 | String[] arr = data.toArray(new String[data.size()]); 71 | Gnomesort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | } catch (IOException ioe) { 74 | fail(ioe.toString()); 75 | } 76 | } 77 | 78 | @Test 79 | public void testReversed() { 80 | try { 81 | List data = Tests.loadData("dictwords", false, MAX_LINES); 82 | Collections.sort(data); 83 | Collections.reverse(data); 84 | String[] arr = data.toArray(new String[data.size()]); 85 | Gnomesort.sort(arr); 86 | assertTrue(Tests.isSorted(arr)); 87 | } catch (IOException ioe) { 88 | fail(ioe.toString()); 89 | } 90 | } 91 | 92 | @Test 93 | public void testRepeated() { 94 | String[] arr = new String[MAX_LINES]; 95 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 96 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 97 | Arrays.fill(arr, STR); 98 | Gnomesort.sort(arr); 99 | assertTrue(Tests.isRepeated(arr, STR)); 100 | } 101 | 102 | @Test 103 | public void testRepeatedCycle() { 104 | String[] strs = new String[100]; 105 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 106 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 107 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 108 | strs[i] = seed.substring(0, l); 109 | } 110 | List list = new ArrayList(); 111 | for (int c = MAX_LINES, i = 0; c > 0; i++, c--) { 112 | list.add(strs[i % strs.length]); 113 | } 114 | String[] arr = list.toArray(new String[list.size()]); 115 | Gnomesort.sort(arr); 116 | assertTrue(Tests.isSorted(arr)); 117 | } 118 | 119 | @Test 120 | public void testRandom() { 121 | List data = Tests.generateData(MAX_LINES, 100); 122 | String[] arr = data.toArray(new String[data.size()]); 123 | Gnomesort.sort(arr); 124 | assertTrue(Tests.isSorted(arr)); 125 | } 126 | 127 | @Test 128 | public void testHamlet() { 129 | try { 130 | List data = Tests.loadData("hamletwords", false, MAX_LINES); 131 | Collections.shuffle(data); 132 | String[] arr = data.toArray(new String[data.size()]); 133 | Gnomesort.sort(arr); 134 | assertTrue(Tests.isSorted(arr)); 135 | } catch (IOException ioe) { 136 | fail(ioe.toString()); 137 | } 138 | } 139 | 140 | @Test 141 | public void testDictCalls() { 142 | try { 143 | List data = Tests.loadData("dictcalls.gz", true, MAX_LINES); 144 | String[] arr = data.toArray(new String[data.size()]); 145 | Gnomesort.sort(arr); 146 | assertTrue(Tests.isSorted(arr)); 147 | } catch (IOException ioe) { 148 | fail(ioe.toString()); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/org/burstsort4j/SelectionsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.util.ArrayList; 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Test the SelectionSort implementations. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class SelectionsortTest { 22 | 23 | /** Maximum number of lines to test for any given file, which avoids 24 | * spending far too much time testing selection sort on data sets 25 | * that it will never actually be called upon to sort in practice. */ 26 | private static final int MAX_LINES = 512; 27 | 28 | @Test 29 | public void testArguments() { 30 | Selectionsort.sort((String[]) null); 31 | Selectionsort.sort(new String[0]); 32 | String[] arr = new String[]{"a"}; 33 | Selectionsort.sort(arr); 34 | arr = new String[]{"b", "a"}; 35 | Selectionsort.sort(arr); 36 | assertTrue(Tests.isSorted(arr)); 37 | arr = new String[]{"c", "b", "a"}; 38 | Selectionsort.sort(arr); 39 | assertTrue(Tests.isSorted(arr)); 40 | // test with all empty input 41 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 42 | Selectionsort.sort(arr); 43 | for (String s : arr) { 44 | assertEquals("", s); 45 | } 46 | // test with peculiar input 47 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 48 | Selectionsort.sort(arr); 49 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 50 | } 51 | 52 | @Test 53 | public void testDictWords() { 54 | try { 55 | List data = Tests.loadData("dictwords", false, MAX_LINES); 56 | Collections.shuffle(data); 57 | String[] arr = data.toArray(new String[data.size()]); 58 | Selectionsort.sort(arr); 59 | assertTrue(Tests.isSorted(arr)); 60 | } catch (IOException ioe) { 61 | fail(ioe.toString()); 62 | } 63 | } 64 | 65 | @Test 66 | public void testSorted() { 67 | try { 68 | List data = Tests.loadData("dictwords", false, MAX_LINES); 69 | Collections.sort(data); 70 | String[] arr = data.toArray(new String[data.size()]); 71 | Selectionsort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | } catch (IOException ioe) { 74 | fail(ioe.toString()); 75 | } 76 | } 77 | 78 | @Test 79 | public void testReversed() { 80 | try { 81 | List data = Tests.loadData("dictwords", false, MAX_LINES); 82 | Collections.sort(data); 83 | Collections.reverse(data); 84 | String[] arr = data.toArray(new String[data.size()]); 85 | Selectionsort.sort(arr); 86 | assertTrue(Tests.isSorted(arr)); 87 | } catch (IOException ioe) { 88 | fail(ioe.toString()); 89 | } 90 | } 91 | 92 | @Test 93 | public void testRepeated() { 94 | String[] arr = new String[MAX_LINES]; 95 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 96 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 97 | Arrays.fill(arr, STR); 98 | Selectionsort.sort(arr); 99 | assertTrue(Tests.isRepeated(arr, STR)); 100 | } 101 | 102 | @Test 103 | public void testRepeatedCycle() { 104 | String[] strs = new String[100]; 105 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 106 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 107 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 108 | strs[i] = seed.substring(0, l); 109 | } 110 | List list = new ArrayList(); 111 | for (int c = MAX_LINES, i = 0; c > 0; i++, c--) { 112 | list.add(strs[i % strs.length]); 113 | } 114 | String[] arr = list.toArray(new String[list.size()]); 115 | Selectionsort.sort(arr); 116 | assertTrue(Tests.isSorted(arr)); 117 | } 118 | 119 | @Test 120 | public void testRandom() { 121 | List data = Tests.generateData(MAX_LINES, 100); 122 | String[] arr = data.toArray(new String[data.size()]); 123 | Selectionsort.sort(arr); 124 | assertTrue(Tests.isSorted(arr)); 125 | } 126 | 127 | @Test 128 | public void testHamlet() { 129 | try { 130 | List data = Tests.loadData("hamletwords", false, MAX_LINES); 131 | Collections.shuffle(data); 132 | String[] arr = data.toArray(new String[data.size()]); 133 | Selectionsort.sort(arr); 134 | assertTrue(Tests.isSorted(arr)); 135 | } catch (IOException ioe) { 136 | fail(ioe.toString()); 137 | } 138 | } 139 | 140 | @Test 141 | public void testDictCalls() { 142 | try { 143 | List data = Tests.loadData("dictcalls.gz", true, MAX_LINES); 144 | String[] arr = data.toArray(new String[data.size()]); 145 | Selectionsort.sort(arr); 146 | assertTrue(Tests.isSorted(arr)); 147 | } catch (IOException ioe) { 148 | fail(ioe.toString()); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/org/burstsort4j/Tests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Random; 15 | import java.util.zip.GZIPInputStream; 16 | 17 | /** 18 | * Collection of utility methods to support the unit tests. 19 | * 20 | * @author Nathan Fiedler 21 | */ 22 | public class Tests { 23 | 24 | /** 25 | * Creates a new instance of Tests. 26 | */ 27 | private Tests() { 28 | } 29 | 30 | /** 31 | * Generates a set of n strings in a List consisting of 32 | * l randomly selected alphanumeric characters (mixed case). 33 | * 34 | * @param n number of random strings to generate. 35 | * @param l length of each random string. 36 | * @return the list of randomly generated strings. 37 | */ 38 | public static List generateData(int n, int l) { 39 | Random r = new Random(); 40 | List list = new ArrayList(); 41 | StringBuilder sb = new StringBuilder(); 42 | for (int ii = 0; ii < n; ii++) { 43 | for (int jj = 0; jj < l; jj++) { 44 | int d = r.nextInt(62); 45 | if (d < 10) { 46 | sb.append((char) ('0' + d)); 47 | } else if (d < 36) { 48 | sb.append((char) ('A' + (d - 10))); 49 | } else { 50 | sb.append((char) ('a' + (d - 36))); 51 | } 52 | } 53 | list.add(sb.toString()); 54 | sb.setLength(0); 55 | } 56 | return list; 57 | } 58 | 59 | /** 60 | * Loads the default test data into a list. 61 | * 62 | * @return test data as a list. 63 | * @throws java.io.IOException 64 | * if a problem occurs. 65 | */ 66 | public static List loadData() throws IOException { 67 | // Note that this dictwords file has different content than the 68 | // compressed version, which is much larger. 69 | return loadData("dictwords", false); 70 | } 71 | 72 | /** 73 | * Loads the specified test data into a list of strings. 74 | * 75 | * @param file name of file to be loaded (must be present in classpath). 76 | * @return test data as a list. 77 | * @throws java.io.IOException 78 | * if a problem occurs. 79 | */ 80 | public static List loadData(String file) throws IOException { 81 | return loadData(file, false); 82 | } 83 | 84 | /** 85 | * Loads the specified test data into a list of strings. 86 | * 87 | * @param file name of file to be loaded (must be present in classpath). 88 | * @param gzip if true, stream will be decompressed using gzip. 89 | * @return test data as a list. 90 | * @throws java.io.IOException 91 | * if a problem occurs. 92 | */ 93 | public static List loadData(String file, boolean gzip) throws IOException { 94 | return loadData(file, gzip, Integer.MAX_VALUE); 95 | } 96 | 97 | /** 98 | * Loads the specified test data into a list of strings. 99 | * 100 | * @param file name of file to be loaded (must be present in classpath). 101 | * @param gzip if true, stream will be decompressed using gzip. 102 | * @param count number of lines to be read. 103 | * @return test data as a list. 104 | * @throws java.io.IOException 105 | * if a problem occurs. 106 | */ 107 | public static List loadData(String file, boolean gzip, int count) throws IOException { 108 | InputStream is = Tests.class.getResourceAsStream(file); 109 | if (gzip) { 110 | is = new GZIPInputStream(is); 111 | } 112 | List list = new ArrayList(); 113 | InputStreamReader isr = new InputStreamReader(is); 114 | BufferedReader br = new BufferedReader(isr); 115 | for (String ln = br.readLine(); ln != null; ln = br.readLine()) { 116 | list.add(ln); 117 | count--; 118 | if (count == 0) { 119 | break; 120 | } 121 | } 122 | br.close(); 123 | return list; 124 | } 125 | 126 | /** 127 | * Tests if the given array of strings is a repeating sequence. 128 | * 129 | * @param arr array of strings to test. 130 | * @param s the expected repeated value. 131 | * @return true if repeating, false otherwise. 132 | */ 133 | public static boolean isRepeated(String[] arr, String s) { 134 | for (int ii = 0; ii < arr.length; ii++) { 135 | if (!arr[ii].equals(s)) { 136 | System.err.format("%s != %s @ %d\n", arr[ii], s, ii); 137 | return false; 138 | } 139 | } 140 | return true; 141 | } 142 | 143 | /** 144 | * Tests if the given array of strings is in sorted order. 145 | * 146 | * @param arr array of strings to test. 147 | * @return true if sorted, false otherwise. 148 | */ 149 | public static boolean isSorted(String[] arr) { 150 | for (int ii = 1; ii < arr.length; ii++) { 151 | if (arr[ii - 1].compareTo(arr[ii]) > 0) { 152 | System.err.format("%s > %s @ %d\n", arr[ii - 1], arr[ii], ii); 153 | return false; 154 | } 155 | } 156 | return true; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /test/org/burstsort4j/ShellsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Shellsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class ShellsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Shellsort.sort((String[]) null); 26 | Shellsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Shellsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Shellsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Shellsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Shellsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Shellsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testSmallSize() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | data = data.subList(0, 10); 53 | String[] arr = data.toArray(new String[data.size()]); 54 | Shellsort.sort(arr); 55 | assertTrue(Tests.isSorted(arr)); 56 | } catch (IOException ioe) { 57 | fail(ioe.toString()); 58 | } 59 | List data = Tests.generateData(10, 100); 60 | String[] arr = data.toArray(new String[data.size()]); 61 | Shellsort.sort(arr); 62 | assertTrue(Tests.isSorted(arr)); 63 | } 64 | 65 | @Test 66 | public void testDictWords() { 67 | try { 68 | List data = Tests.loadData(); 69 | Collections.shuffle(data); 70 | String[] arr = data.toArray(new String[data.size()]); 71 | Shellsort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | } catch (IOException ioe) { 74 | fail(ioe.toString()); 75 | } 76 | } 77 | 78 | @Test 79 | public void testSorted() { 80 | try { 81 | List data = Tests.loadData(); 82 | Collections.sort(data); 83 | String[] arr = data.toArray(new String[data.size()]); 84 | Shellsort.sort(arr); 85 | assertTrue(Tests.isSorted(arr)); 86 | } catch (IOException ioe) { 87 | fail(ioe.toString()); 88 | } 89 | } 90 | 91 | @Test 92 | public void testReversed() { 93 | try { 94 | List data = Tests.loadData(); 95 | Collections.sort(data); 96 | Collections.reverse(data); 97 | String[] arr = data.toArray(new String[data.size()]); 98 | Shellsort.sort(arr); 99 | assertTrue(Tests.isSorted(arr)); 100 | } catch (IOException ioe) { 101 | fail(ioe.toString()); 102 | } 103 | } 104 | 105 | @Test 106 | public void testRepeated() { 107 | String[] arr = new String[10000]; 108 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 109 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 110 | Arrays.fill(arr, STR); 111 | Shellsort.sort(arr); 112 | assertTrue(Tests.isRepeated(arr, STR)); 113 | } 114 | 115 | @Test 116 | public void testRepeatedCycle() { 117 | String[] strs = new String[100]; 118 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 119 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 120 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 121 | strs[i] = seed.substring(0, l); 122 | } 123 | List list = new ArrayList(); 124 | for (int c = 10000, i = 0; c > 0; i++, c--) { 125 | list.add(strs[i % strs.length]); 126 | } 127 | String[] arr = list.toArray(new String[list.size()]); 128 | Shellsort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } 131 | 132 | @Test 133 | public void testRandom() { 134 | List data = Tests.generateData(10000, 100); 135 | String[] arr = data.toArray(new String[data.size()]); 136 | Shellsort.sort(arr); 137 | assertTrue(Tests.isSorted(arr)); 138 | } 139 | 140 | @Test 141 | public void testHamlet() { 142 | try { 143 | List data = Tests.loadData("hamletwords"); 144 | Collections.shuffle(data); 145 | String[] arr = data.toArray(new String[data.size()]); 146 | Shellsort.sort(arr); 147 | assertTrue(Tests.isSorted(arr)); 148 | } catch (IOException ioe) { 149 | fail(ioe.toString()); 150 | } 151 | } 152 | 153 | @Test 154 | public void testDictCalls() { 155 | try { 156 | List data = Tests.loadData("dictcalls.gz", true); 157 | String[] arr = data.toArray(new String[data.size()]); 158 | Shellsort.sort(arr); 159 | assertTrue(Tests.isSorted(arr)); 160 | } catch (IOException ioe) { 161 | fail(ioe.toString()); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /test/org/burstsort4j/HeapsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Heapsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class HeapsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Heapsort.sort((String[]) null); 26 | Heapsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Heapsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Heapsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Heapsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Heapsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Heapsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testSmallSize() { 49 | try { 50 | List data = Tests.loadData(); 51 | Collections.shuffle(data); 52 | data = data.subList(0, 10); 53 | String[] arr = data.toArray(new String[data.size()]); 54 | Heapsort.sort(arr); 55 | assertTrue(Tests.isSorted(arr)); 56 | } catch (IOException ioe) { 57 | fail(ioe.toString()); 58 | } 59 | List data = Tests.generateData(10, 100); 60 | String[] arr = data.toArray(new String[data.size()]); 61 | Heapsort.sort(arr); 62 | assertTrue(Tests.isSorted(arr)); 63 | } 64 | 65 | @Test 66 | public void testDictWords() { 67 | try { 68 | List data = Tests.loadData(); 69 | Collections.shuffle(data); 70 | String[] arr = data.toArray(new String[data.size()]); 71 | Heapsort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | } catch (IOException ioe) { 74 | fail(ioe.toString()); 75 | } 76 | } 77 | 78 | @Test 79 | public void testSorted() { 80 | try { 81 | List data = Tests.loadData(); 82 | Collections.sort(data); 83 | String[] arr = data.toArray(new String[data.size()]); 84 | Heapsort.sort(arr); 85 | assertTrue(Tests.isSorted(arr)); 86 | } catch (IOException ioe) { 87 | fail(ioe.toString()); 88 | } 89 | } 90 | 91 | @Test 92 | public void testReversed() { 93 | try { 94 | List data = Tests.loadData(); 95 | Collections.sort(data); 96 | Collections.reverse(data); 97 | String[] arr = data.toArray(new String[data.size()]); 98 | Heapsort.sort(arr); 99 | assertTrue(Tests.isSorted(arr)); 100 | } catch (IOException ioe) { 101 | fail(ioe.toString()); 102 | } 103 | } 104 | 105 | @Test 106 | public void testRepeated() { 107 | String[] arr = new String[10000]; 108 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 109 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 110 | Arrays.fill(arr, STR); 111 | Heapsort.sort(arr); 112 | assertTrue(Tests.isRepeated(arr, STR)); 113 | } 114 | 115 | @Test 116 | public void testRepeatedCycle() { 117 | String[] strs = new String[100]; 118 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 119 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 120 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 121 | strs[i] = seed.substring(0, l); 122 | } 123 | List list = new ArrayList(); 124 | for (int c = 10000, i = 0; c > 0; i++, c--) { 125 | list.add(strs[i % strs.length]); 126 | } 127 | String[] arr = list.toArray(new String[list.size()]); 128 | Heapsort.sort(arr); 129 | assertTrue(Tests.isSorted(arr)); 130 | } 131 | 132 | @Test 133 | public void testRandom() { 134 | List data = Tests.generateData(10000, 100); 135 | String[] arr = data.toArray(new String[data.size()]); 136 | Heapsort.sort(arr); 137 | assertTrue(Tests.isSorted(arr)); 138 | } 139 | 140 | @Test 141 | public void testHamlet() { 142 | try { 143 | List data = Tests.loadData("hamletwords"); 144 | Collections.shuffle(data); 145 | String[] arr = data.toArray(new String[data.size()]); 146 | Heapsort.sort(arr); 147 | assertTrue(Tests.isSorted(arr)); 148 | } catch (IOException ioe) { 149 | fail(ioe.toString()); 150 | } 151 | } 152 | 153 | // @Test -- takes much too long to finish 154 | public void testDictCalls() { 155 | try { 156 | List data = Tests.loadData("dictcalls.gz", true); 157 | String[] arr = data.toArray(new String[data.size()]); 158 | Heapsort.sort(arr); 159 | assertTrue(Tests.isSorted(arr)); 160 | } catch (IOException ioe) { 161 | fail(ioe.toString()); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2012 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.BufferedWriter; 10 | import java.io.FileReader; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import java.io.InputStreamReader; 17 | import java.io.OutputStreamWriter; 18 | import java.util.zip.GZIPInputStream; 19 | import java.util.zip.GZIPOutputStream; 20 | import java.io.FileInputStream; 21 | import java.io.FileOutputStream; 22 | import java.io.Reader; 23 | import java.io.Writer; 24 | 25 | /** 26 | * Command-line interface to the various sort implementations. 27 | * 28 | * @author Nathan Fiedler 29 | */ 30 | public class Main { 31 | 32 | private static final String BURSTSORT = "--burstsort"; 33 | private static final String FUNNELSORT = "--funnelsort"; 34 | private static final String MULTIKEY = "--multikey"; 35 | 36 | private Main() { 37 | } 38 | 39 | /** 40 | * Main entry point for the sorter. 41 | * 42 | * @param args command line arguments. 43 | */ 44 | public static void main(String[] args) { 45 | if (args.length < 2 || args.length > 3) { 46 | usage(); 47 | } 48 | String input; 49 | String output; 50 | String sort; 51 | if (args.length == 3) { 52 | sort = args[0]; 53 | input = args[1]; 54 | output = args[2]; 55 | } else { 56 | sort = BURSTSORT; 57 | input = args[0]; 58 | output = args[1]; 59 | } 60 | 61 | // Read in the input file. 62 | long r1 = System.currentTimeMillis(); 63 | String[] data = readFile(input); 64 | long r2 = System.currentTimeMillis(); 65 | System.out.format("Read time: %dms\n", r2 - r1); 66 | 67 | // Sort the data using the selected sort. 68 | long s1 = System.currentTimeMillis(); 69 | if (sort.equals(FUNNELSORT)) { 70 | LazyFunnelsort.sort(data); 71 | } else if (sort.equals(BURSTSORT)) { 72 | Burstsort.sort(data); 73 | } else if (sort.equals(MULTIKEY)) { 74 | MultikeyQuicksort.sort(data); 75 | } else { 76 | usage(); 77 | } 78 | long s2 = System.currentTimeMillis(); 79 | System.out.format("Sort time: %dms\n", s2 - s1); 80 | 81 | // Write the results to the output file. 82 | long w1 = System.currentTimeMillis(); 83 | writeFile(output, data); 84 | long w2 = System.currentTimeMillis(); 85 | System.out.format("Write time: %dms\n", w2 - w1); 86 | } 87 | 88 | /** 89 | * Display usage information and exit. 90 | */ 91 | private static void usage() { 92 | System.out.println("Usage: Main [options] \n"); 93 | System.out.println("\t is the name of the (unsorted) input file."); 94 | System.out.println("\t is the name for the (sorted) output file."); 95 | System.out.println("\tGzip compression will be used on any file having a \".gz\" suffix\n"); 96 | System.out.println("\t--burstsort"); 97 | System.out.println("\t\tSort using the original Burstsort algorithm."); 98 | System.out.println("\t\tThis is the default sort if none is specified.\n"); 99 | System.out.println("\t--funnelsort"); 100 | System.out.println("\t\tSort using the Lazy Funnelsort algorithm.\n"); 101 | System.out.println("\t--multikey"); 102 | System.out.println("\t\tSort using the Multikey Quicksort algorithm."); 103 | System.exit(0); 104 | } 105 | 106 | /** 107 | * Reads the contents of the named file into an array of strings. 108 | * 109 | * @param name file to be read. 110 | * @return the lines of text from the file. 111 | */ 112 | private static String[] readFile(String name) { 113 | List data = new ArrayList(); 114 | try { 115 | Reader fr; 116 | if (name.toLowerCase().endsWith(".gz")) { 117 | FileInputStream fis = new FileInputStream(name); 118 | GZIPInputStream gis = new GZIPInputStream(fis); 119 | fr = new InputStreamReader(gis); 120 | } else { 121 | fr = new FileReader(name); 122 | } 123 | BufferedReader br = new BufferedReader(fr); 124 | String line = br.readLine(); 125 | while (line != null) { 126 | data.add(line); 127 | line = br.readLine(); 128 | } 129 | br.close(); 130 | } catch (IOException ioe) { 131 | ioe.printStackTrace(); 132 | } 133 | return data.toArray(new String[data.size()]); 134 | } 135 | 136 | /** 137 | * Write the given array of strings to the named file. 138 | * 139 | * @param name file name to write to. 140 | * @param data the strings to be written. 141 | */ 142 | private static void writeFile(String name, String[] data) { 143 | try { 144 | Writer fw; 145 | if (name.toLowerCase().endsWith(".gz")) { 146 | FileOutputStream fos = new FileOutputStream(name); 147 | GZIPOutputStream gos = new GZIPOutputStream(fos); 148 | fw = new OutputStreamWriter(gos); 149 | } else { 150 | fw = new FileWriter(name); 151 | } 152 | BufferedWriter bw = new BufferedWriter(fw); 153 | for (String output : data) { 154 | bw.write(output); 155 | bw.newLine(); 156 | } 157 | bw.close(); 158 | } catch (IOException ioe) { 159 | ioe.printStackTrace(); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /nbproject/profiler-build-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Must set JVM to use for profiling in profiler.info.jvm 63 | Must set profiler agent JVM arguments in profiler.info.jvmargs.agent 64 | 65 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | Must select one file in the IDE or set profile.class 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /test/org/burstsort4j/InsertionsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import org.junit.Test; 13 | import static org.junit.Assert.*; 14 | 15 | /** 16 | * Test the insertion sort implementations. 17 | * 18 | * @author Nathan Fiedler 19 | */ 20 | public class InsertionsortTest { 21 | 22 | /** Maximum number of lines to test for any given file, which avoids 23 | * spending far too much time testing insertion sort on data sets 24 | * that it will never actually be called upon to sort in practice. */ 25 | private static final int MAX_LINES = 512; 26 | 27 | @Test 28 | public void testArguments() { 29 | Insertionsort.sort((String[]) null); 30 | Insertionsort.sort(new String[0]); 31 | String[] arr = new String[]{"a"}; 32 | Insertionsort.sort(arr); 33 | arr = new String[]{"b", "a"}; 34 | Insertionsort.sort(arr); 35 | assertTrue(Tests.isSorted(arr)); 36 | arr = new String[]{"c", "b", "a"}; 37 | Insertionsort.sort(arr); 38 | assertTrue(Tests.isSorted(arr)); 39 | // test with all empty input 40 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 41 | Insertionsort.sort(arr); 42 | for (String s : arr) { 43 | assertEquals("", s); 44 | } 45 | // test with peculiar input 46 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 47 | Insertionsort.sort(arr); 48 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 49 | } 50 | 51 | @Test 52 | public void testComparable() { 53 | try { 54 | List data = Tests.loadData("dictwords", false, MAX_LINES); 55 | Collections.shuffle(data); 56 | String[] arr = data.toArray(new String[data.size()]); 57 | Insertionsort.sort(arr); 58 | assertTrue(Tests.isSorted(arr)); 59 | // Test with sorted list 60 | Insertionsort.sort(arr); 61 | assertTrue(Tests.isSorted(arr)); 62 | // Test with reverse sorted list 63 | Collections.reverse(data); 64 | arr = data.toArray(new String[data.size()]); 65 | Insertionsort.sort(arr); 66 | assertTrue(Tests.isSorted(arr)); 67 | // Test with non-unique word list. 68 | data = Tests.loadData("hamletwords", false, MAX_LINES); 69 | Collections.shuffle(data); 70 | arr = data.toArray(new String[data.size()]); 71 | Insertionsort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | // Test with sorted list 74 | Insertionsort.sort(arr); 75 | assertTrue(Tests.isSorted(arr)); 76 | // Test with reverse sorted list 77 | Collections.reverse(data); 78 | arr = data.toArray(new String[data.size()]); 79 | Insertionsort.sort(arr); 80 | assertTrue(Tests.isSorted(arr)); 81 | } catch (IOException ioe) { 82 | fail(ioe.toString()); 83 | } 84 | // Test with repeated strings. 85 | String[] arr = new String[MAX_LINES]; 86 | Arrays.fill(arr, "abcdefghijklmnopqrstuvwxyz"); 87 | Insertionsort.sort(arr); 88 | assertTrue(Tests.isRepeated(arr, "abcdefghijklmnopqrstuvwxyz")); 89 | // Test with randomly generated strings. 90 | List data = Tests.generateData(MAX_LINES, 64); 91 | arr = data.toArray(new String[data.size()]); 92 | Insertionsort.sort(arr); 93 | assertTrue(Tests.isSorted(arr)); 94 | } 95 | 96 | @Test 97 | public void testStrings() { 98 | try { 99 | List data = Tests.loadData("dictwords", false, MAX_LINES); 100 | Collections.shuffle(data); 101 | String[] arr = data.toArray(new String[data.size()]); 102 | Insertionsort.sort(arr, 0, arr.length, 0); 103 | assertTrue(Tests.isSorted(arr)); 104 | // Test with sorted list 105 | Insertionsort.sort(arr, 0, arr.length, 0); 106 | assertTrue(Tests.isSorted(arr)); 107 | // Test with reverse sorted list 108 | Collections.reverse(data); 109 | arr = data.toArray(new String[data.size()]); 110 | Insertionsort.sort(arr, 0, arr.length, 0); 111 | assertTrue(Tests.isSorted(arr)); 112 | // Test with non-unique word list. 113 | data = Tests.loadData("hamletwords", false, MAX_LINES); 114 | Collections.shuffle(data); 115 | arr = data.toArray(new String[data.size()]); 116 | Insertionsort.sort(arr, 0, arr.length, 0); 117 | assertTrue(Tests.isSorted(arr)); 118 | // Test with sorted list 119 | Insertionsort.sort(arr, 0, arr.length, 0); 120 | assertTrue(Tests.isSorted(arr)); 121 | // Test with reverse sorted list 122 | Collections.reverse(data); 123 | arr = data.toArray(new String[data.size()]); 124 | Insertionsort.sort(arr, 0, arr.length, 0); 125 | assertTrue(Tests.isSorted(arr)); 126 | } catch (IOException ioe) { 127 | fail(ioe.toString()); 128 | } 129 | // Test with repeated strings. 130 | String[] arr = new String[MAX_LINES]; 131 | Arrays.fill(arr, "abcdefghijklmnopqrstuvwxyz"); 132 | Insertionsort.sort(arr, 0, arr.length, 0); 133 | assertTrue(Tests.isRepeated(arr, "abcdefghijklmnopqrstuvwxyz")); 134 | // Test with randomly generated strings. 135 | List data = Tests.generateData(MAX_LINES, 64); 136 | arr = data.toArray(new String[data.size()]); 137 | Insertionsort.sort(arr, 0, arr.length, 0); 138 | assertTrue(Tests.isSorted(arr)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /test/org/burstsort4j/BinaryInsertionsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import org.junit.Test; 13 | import static org.junit.Assert.*; 14 | 15 | /** 16 | * Test the binary insertion sort implementations. 17 | * 18 | * @author Nathan Fiedler 19 | */ 20 | public class BinaryInsertionsortTest { 21 | 22 | /** Maximum number of lines to test for any given file, which avoids 23 | * spending far too much time testing insertion sort on data sets 24 | * that it will never actually be called upon to sort in practice. */ 25 | private static final int MAX_LINES = 512; 26 | 27 | @Test 28 | public void testArguments() { 29 | BinaryInsertionsort.sort((String[]) null); 30 | BinaryInsertionsort.sort(new String[0]); 31 | String[] arr = new String[]{"a"}; 32 | BinaryInsertionsort.sort(arr); 33 | arr = new String[]{"b", "a"}; 34 | BinaryInsertionsort.sort(arr); 35 | assertTrue(Tests.isSorted(arr)); 36 | arr = new String[]{"c", "b", "a"}; 37 | BinaryInsertionsort.sort(arr); 38 | assertTrue(Tests.isSorted(arr)); 39 | // test with all empty input 40 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 41 | BinaryInsertionsort.sort(arr); 42 | for (String s : arr) { 43 | assertEquals("", s); 44 | } 45 | // test with peculiar input 46 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 47 | BinaryInsertionsort.sort(arr); 48 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 49 | } 50 | 51 | @Test 52 | public void testComparable() { 53 | try { 54 | List data = Tests.loadData("dictwords", false, MAX_LINES); 55 | Collections.shuffle(data); 56 | String[] arr = data.toArray(new String[data.size()]); 57 | BinaryInsertionsort.sort(arr); 58 | assertTrue(Tests.isSorted(arr)); 59 | // Test with sorted list 60 | BinaryInsertionsort.sort(arr); 61 | assertTrue(Tests.isSorted(arr)); 62 | // Test with reverse sorted list 63 | Collections.reverse(data); 64 | arr = data.toArray(new String[data.size()]); 65 | BinaryInsertionsort.sort(arr); 66 | assertTrue(Tests.isSorted(arr)); 67 | // Test with non-unique word list. 68 | data = Tests.loadData("hamletwords", false, MAX_LINES); 69 | Collections.shuffle(data); 70 | arr = data.toArray(new String[data.size()]); 71 | BinaryInsertionsort.sort(arr); 72 | assertTrue(Tests.isSorted(arr)); 73 | // Test with sorted list 74 | BinaryInsertionsort.sort(arr); 75 | assertTrue(Tests.isSorted(arr)); 76 | // Test with reverse sorted list 77 | Collections.reverse(data); 78 | arr = data.toArray(new String[data.size()]); 79 | BinaryInsertionsort.sort(arr); 80 | assertTrue(Tests.isSorted(arr)); 81 | } catch (IOException ioe) { 82 | fail(ioe.toString()); 83 | } 84 | // Test with repeated strings. 85 | String[] arr = new String[MAX_LINES]; 86 | Arrays.fill(arr, "abcdefghijklmnopqrstuvwxyz"); 87 | BinaryInsertionsort.sort(arr); 88 | assertTrue(Tests.isRepeated(arr, "abcdefghijklmnopqrstuvwxyz")); 89 | // Test with randomly generated strings. 90 | List data = Tests.generateData(MAX_LINES, 64); 91 | arr = data.toArray(new String[data.size()]); 92 | BinaryInsertionsort.sort(arr); 93 | assertTrue(Tests.isSorted(arr)); 94 | } 95 | 96 | @Test 97 | public void testStrings() { 98 | try { 99 | List data = Tests.loadData("dictwords", false, MAX_LINES); 100 | Collections.shuffle(data); 101 | String[] arr = data.toArray(new String[data.size()]); 102 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 103 | assertTrue(Tests.isSorted(arr)); 104 | // Test with sorted list 105 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 106 | assertTrue(Tests.isSorted(arr)); 107 | // Test with reverse sorted list 108 | Collections.reverse(data); 109 | arr = data.toArray(new String[data.size()]); 110 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 111 | assertTrue(Tests.isSorted(arr)); 112 | // Test with non-unique word list. 113 | data = Tests.loadData("hamletwords", false, MAX_LINES); 114 | Collections.shuffle(data); 115 | arr = data.toArray(new String[data.size()]); 116 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 117 | assertTrue(Tests.isSorted(arr)); 118 | // Test with sorted list 119 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 120 | assertTrue(Tests.isSorted(arr)); 121 | // Test with reverse sorted list 122 | Collections.reverse(data); 123 | arr = data.toArray(new String[data.size()]); 124 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 125 | assertTrue(Tests.isSorted(arr)); 126 | } catch (IOException ioe) { 127 | fail(ioe.toString()); 128 | } 129 | // Test with repeated strings. 130 | String[] arr = new String[MAX_LINES]; 131 | Arrays.fill(arr, "abcdefghijklmnopqrstuvwxyz"); 132 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 133 | assertTrue(Tests.isRepeated(arr, "abcdefghijklmnopqrstuvwxyz")); 134 | // Test with randomly generated strings. 135 | List data = Tests.generateData(MAX_LINES, 64); 136 | arr = data.toArray(new String[data.size()]); 137 | BinaryInsertionsort.sort(arr, 0, arr.length, 0); 138 | assertTrue(Tests.isSorted(arr)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/org/burstsort4j/BinaryInsertionsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Implementation of binary insertion sort algorithm borrowed from tim sort, 10 | * with some minor modifications. 11 | * 12 | * @author Nathan Fiedler 13 | */ 14 | public class BinaryInsertionsort { 15 | 16 | private BinaryInsertionsort() { 17 | } 18 | 19 | /** 20 | * Sorts the specified portion of the array using a binary insertion sort. 21 | * This is the best method for sorting small numbers of elements. 22 | * It requires O(n log n) compares, but O(n^2) data movement (worst case). 23 | * 24 | * @param type of comparable to be sorted. 25 | * @param arr the array in which a range is to be sorted. 26 | */ 27 | public static > void sort(T[] arr) { 28 | if (arr != null) { 29 | sort(arr, 0, arr.length - 1); 30 | } 31 | } 32 | 33 | /** 34 | * Sorts the specified portion of the array using a binary insertion sort. 35 | * This is the best method for sorting small numbers of elements. 36 | * It requires O(n log n) compares, but O(n^2) data movement (worst case). 37 | * 38 | * @param type of comparable to be sorted. 39 | * @param arr the array in which a range is to be sorted. 40 | * @param low the index of the first element in the range to be sorted. 41 | * @param high the index of the last element in the range to be sorted. 42 | */ 43 | @SuppressWarnings("fallthrough") 44 | public static > void sort( 45 | T[] arr, int low, int high) { 46 | if (arr == null || arr.length < 2 || low < 0 || high <= low) { 47 | return; 48 | } 49 | for (int ii = low; ii <= high; ii++) { 50 | T pivot = arr[ii]; 51 | 52 | // Set left (and right) to the index where a[start] (pivot) belongs 53 | int left = low; 54 | int right = ii; 55 | // Invariants: 56 | // pivot >= all in [lo, left). 57 | // pivot < all in [right, start). 58 | while (left < right) { 59 | int mid = (left + right) >>> 1; 60 | if (pivot.compareTo(arr[mid]) < 0) { 61 | right = mid; 62 | } else { 63 | left = mid + 1; 64 | } 65 | } 66 | 67 | // The invariants above still hold, so pivot belongs at left. 68 | // Note that if there are elements equal to pivot, left points 69 | // to the first slot after them -- that's why this sort is stable. 70 | // Slide elements over to make room to make room for pivot. 71 | int count = ii - left; 72 | // Switch is just an optimization for arraycopy in default case. 73 | switch (count) { 74 | case 2: 75 | arr[left + 2] = arr[left + 1]; 76 | case 1: 77 | arr[left + 1] = arr[left]; 78 | break; 79 | default: 80 | System.arraycopy(arr, left, arr, left + 1, count); 81 | } 82 | arr[left] = pivot; 83 | } 84 | } 85 | 86 | /** 87 | * Sorts the specified portion of the array using a binary insertion sort. 88 | * This is the best method for sorting small numbers of elements. 89 | * It requires O(n log n) compares, but O(n^2) data movement (worst case). 90 | * 91 | * @param arr the array in which a range is to be sorted. 92 | * @param low low offset into the array (inclusive). 93 | * @param high high offset into the array (exclusive). 94 | * @param depth offset of first character in each string to compare. 95 | */ 96 | @SuppressWarnings("fallthrough") 97 | public static void sort(CharSequence[] arr, int low, int high, int depth) { 98 | if (arr == null || arr.length < 2 || low < 0 || high <= low || depth < 0) { 99 | return; 100 | } 101 | for (int ii = low; ii < high; ii++) { 102 | CharSequence pivot = arr[ii]; 103 | 104 | // Set left (and right) to the index where a[start] (pivot) belongs 105 | int left = low; 106 | int right = ii; 107 | // Invariants: 108 | // pivot >= all in [lo, left). 109 | // pivot < all in [right, start). 110 | while (left < right) { 111 | int mid = (left + right) >>> 1; 112 | if (compare(pivot, arr[mid], depth) < 0) { 113 | right = mid; 114 | } else { 115 | left = mid + 1; 116 | } 117 | } 118 | 119 | // The invariants above still hold, so pivot belongs at left. 120 | // Note that if there are elements equal to pivot, left points 121 | // to the first slot after them -- that's why this sort is stable. 122 | // Slide elements over to make room to make room for pivot. 123 | int count = ii - left; 124 | // Switch is just an optimization for arraycopy in default case. 125 | switch (count) { 126 | case 2: 127 | arr[left + 2] = arr[left + 1]; 128 | case 1: 129 | arr[left + 1] = arr[left]; 130 | break; 131 | default: 132 | System.arraycopy(arr, left, arr, left + 1, count); 133 | } 134 | arr[left] = pivot; 135 | } 136 | } 137 | 138 | /** 139 | * Compare two character sequences, starting with the characters at 140 | * offset depth (assumes the leading characters are the 141 | * same in both sequences). 142 | * 143 | * @param a first character sequence to compare. 144 | * @param b second character sequence to compare. 145 | * @param depth offset of first character in each string to compare. 146 | * @return a negative integer, zero, or a positive integer as the first 147 | * argument is less than, equal to, or greater than the second. 148 | */ 149 | private static int compare(CharSequence a, CharSequence b, int depth) { 150 | int idx = depth; 151 | char s = idx < a.length() ? a.charAt(idx) : 0; 152 | char t = idx < b.length() ? b.charAt(idx) : 0; 153 | while (s == t && idx < a.length()) { 154 | idx++; 155 | s = idx < a.length() ? a.charAt(idx) : 0; 156 | t = idx < b.length() ? b.charAt(idx) : 0; 157 | } 158 | return s - t; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/org/burstsort4j/DualPivotQuicksort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2012 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * DualPivotQuickSort will sort the given slice of strings using the 10 | * two pivot value quicksort variation by Vladimir Yaroslavskiy. This 11 | * version if a translation of the Go version, which itself is a 12 | * translation of a Java version written by Yaroslavskiy. 13 | */ 14 | public class DualPivotQuicksort { 15 | 16 | private DualPivotQuicksort() { 17 | } 18 | 19 | /** 20 | * Sorts the given array of comparable objects using the dual-pivot 21 | * quicksort algorithm. 22 | * 23 | * @param arr an array of Comparable items to sort. 24 | */ 25 | public static void sort(String[] arr) { 26 | if (arr != null && arr.length > 1) { 27 | sort(arr, 0, arr.length - 1); 28 | } 29 | } 30 | 31 | /** 32 | * Sorts the given array of comparable objects using the dual-pivot 33 | * quicksort algorithm within the given range of the array. 34 | * 35 | * @param type of comparable to be sorted. 36 | * @param arr an array of Comparable items to sort. 37 | * @param left lower bound of range to be sorted. 38 | * @param right upper bound of range to be sorted. 39 | */ 40 | private static void sort(String[] arr, int left, int right) { 41 | int len = right - left; 42 | 43 | // perform insertion sort on small ranges 44 | if (len < 17) { 45 | for (int i = left + 1; i <= right; i++) { 46 | for (int j = i; j > left && arr[j].compareTo(arr[j - 1]) < 0; j--) { 47 | String t = arr[j - 1]; 48 | arr[j - 1] = arr[j]; 49 | arr[j] = t; 50 | } 51 | } 52 | return; 53 | } 54 | 55 | // compute indices of medians 56 | int sixth = len / 6; 57 | int m1 = left + sixth; 58 | int m2 = m1 + sixth; 59 | int m3 = m2 + sixth; 60 | int m4 = m3 + sixth; 61 | int m5 = m4 + sixth; 62 | 63 | // order the medians in preparation for partitioning 64 | if (arr[m1].compareTo(arr[m2]) > 0) { 65 | String t = arr[m1]; 66 | arr[m1] = arr[m2]; 67 | arr[m2] = t; 68 | } 69 | if (arr[m4].compareTo(arr[m5]) > 0) { 70 | String t = arr[m4]; 71 | arr[m4] = arr[m5]; 72 | arr[m4] = t; 73 | } 74 | if (arr[m1].compareTo(arr[m3]) > 0) { 75 | String t = arr[m1]; 76 | arr[m1] = arr[m3]; 77 | arr[m3] = t; 78 | } 79 | if (arr[m2].compareTo(arr[m3]) > 0) { 80 | String t = arr[m2]; 81 | arr[m2] = arr[m3]; 82 | arr[m3] = t; 83 | } 84 | if (arr[m1].compareTo(arr[m4]) > 0) { 85 | String t = arr[m1]; 86 | arr[m1] = arr[m4]; 87 | arr[m4] = t; 88 | } 89 | if (arr[m3].compareTo(arr[m4]) > 0) { 90 | String t = arr[m3]; 91 | arr[m3] = arr[m4]; 92 | arr[m4] = t; 93 | } 94 | if (arr[m2].compareTo(arr[m5]) > 0) { 95 | String t = arr[m2]; 96 | arr[m2] = arr[m5]; 97 | arr[m5] = t; 98 | } 99 | if (arr[m2].compareTo(arr[m3]) > 0) { 100 | String t = arr[m2]; 101 | arr[m2] = arr[m3]; 102 | arr[m3] = t; 103 | } 104 | if (arr[m4].compareTo(arr[m5]) > 0) { 105 | String t = arr[m4]; 106 | arr[m4] = arr[m5]; 107 | arr[m5] = t; 108 | } 109 | 110 | // select the pivots such that [ < pivot1 | pivot1 <= && <= pivot2 | > pivot2 ] 111 | String pivot1 = arr[m2]; 112 | String pivot2 = arr[m4]; 113 | 114 | boolean diffPivots = !pivot1.equals(pivot2); 115 | 116 | // move the pivots out of the away 117 | arr[m2] = arr[left]; 118 | arr[m4] = arr[right]; 119 | int less = left + 1; 120 | int more = right - 1; 121 | 122 | // partition the elements 123 | if (diffPivots) { 124 | for (int k = less; k <= more; k++) { 125 | String x = arr[k]; 126 | if (x.compareTo(pivot2) > 0) { 127 | while (arr[more].compareTo(pivot2) > 0 && k < more) { 128 | more--; 129 | } 130 | arr[k] = arr[more]; 131 | arr[more] = x; 132 | more--; 133 | x = arr[k]; 134 | } 135 | if (x.compareTo(pivot1) < 0) { 136 | arr[k] = arr[less]; 137 | arr[less] = x; 138 | less++; 139 | } 140 | } 141 | } else { 142 | for (int k = less; k <= more; k++) { 143 | String x = arr[k]; 144 | if (x.equals(pivot1)) { 145 | continue; 146 | } 147 | if (x.compareTo(pivot1) > 0) { 148 | while (arr[more].compareTo(pivot2) > 0 && k < more) { 149 | more--; 150 | } 151 | arr[k] = arr[more]; 152 | arr[more] = x; 153 | more--; 154 | x = arr[k]; 155 | } 156 | if (x.compareTo(pivot1) < 0) { 157 | arr[k] = arr[less]; 158 | arr[less] = x; 159 | less++; 160 | } 161 | } 162 | } 163 | 164 | // swap the pivots back into position 165 | arr[left] = arr[less - 1]; 166 | arr[less - 1] = pivot1; 167 | arr[right] = arr[more + 1]; 168 | arr[more + 1] = pivot2; 169 | 170 | // recursively sort the left and right partitions 171 | sort(arr, left, less - 2); 172 | sort(arr, more + 2, right); 173 | 174 | // order the equal elements in the middle 175 | if (more - less > len - 13 && diffPivots) { 176 | for (int k = less; k <= more; k++) { 177 | String x = arr[k]; 178 | if (x.equals(pivot2)) { 179 | arr[k] = arr[more]; 180 | arr[more] = x; 181 | more--; 182 | x = arr[k]; 183 | } 184 | if (x.equals(pivot1)) { 185 | arr[k] = arr[less]; 186 | arr[less] = x; 187 | less++; 188 | } 189 | } 190 | } 191 | 192 | // recursively sort the middle partition 193 | if (diffPivots) { 194 | sort(arr, less, more); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/SortRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import java.util.Arrays; 9 | import org.burstsort4j.BinaryInsertionsort; 10 | import org.burstsort4j.Burstsort; 11 | import org.burstsort4j.Combsort; 12 | import org.burstsort4j.DualPivotQuicksort; 13 | import org.burstsort4j.Gnomesort; 14 | import org.burstsort4j.Heapsort; 15 | import org.burstsort4j.HybridCombsort; 16 | import org.burstsort4j.Insertionsort; 17 | import org.burstsort4j.Introsort; 18 | import org.burstsort4j.LazyFunnelsort; 19 | import org.burstsort4j.MultikeyQuicksort; 20 | import org.burstsort4j.Quicksort; 21 | import org.burstsort4j.RedesignedBurstsort; 22 | import org.burstsort4j.Selectionsort; 23 | import org.burstsort4j.Shellsort; 24 | 25 | /** 26 | * Runs a particular sort implementation. 27 | * 28 | * @author Nathan Fiedler 29 | */ 30 | public enum SortRunner { 31 | 32 | COMB { 33 | 34 | @Override 35 | public String getDisplayName() { 36 | return "Combsort"; 37 | } 38 | 39 | @Override 40 | public void sort(String[] data) { 41 | Combsort.sort(data); 42 | } 43 | }, 44 | HYBRID_COMB { 45 | 46 | @Override 47 | public String getDisplayName() { 48 | return "HybridComb"; 49 | } 50 | 51 | @Override 52 | public void sort(String[] data) { 53 | HybridCombsort.sort(data); 54 | } 55 | }, 56 | GNOME { 57 | 58 | @Override 59 | public String getDisplayName() { 60 | return "Gnomesort"; 61 | } 62 | 63 | @Override 64 | public void sort(String[] data) { 65 | Gnomesort.sort(data); 66 | } 67 | }, 68 | HEAP { 69 | 70 | @Override 71 | public String getDisplayName() { 72 | return "Heapsort"; 73 | } 74 | 75 | @Override 76 | public void sort(String[] data) { 77 | Heapsort.sort(data); 78 | } 79 | }, 80 | INSERTION { 81 | 82 | @Override 83 | public String getDisplayName() { 84 | return "Insertionsort"; 85 | } 86 | 87 | @Override 88 | public void sort(String[] data) { 89 | Insertionsort.sort(data); 90 | } 91 | }, 92 | BINARY_INSERTION { 93 | 94 | @Override 95 | public String getDisplayName() { 96 | return "BinInsertionsort"; 97 | } 98 | 99 | @Override 100 | public void sort(String[] data) { 101 | BinaryInsertionsort.sort(data); 102 | } 103 | }, 104 | INTRO { 105 | 106 | @Override 107 | public String getDisplayName() { 108 | return "Introsort"; 109 | } 110 | 111 | @Override 112 | public void sort(String[] data) { 113 | Introsort.sort(data); 114 | } 115 | }, 116 | QUICK { 117 | 118 | @Override 119 | public String getDisplayName() { 120 | return "Quicksort"; 121 | } 122 | 123 | @Override 124 | public void sort(String[] data) { 125 | Quicksort.sort(data); 126 | } 127 | }, 128 | QUICK_2_PIVOT { 129 | 130 | @Override 131 | public String getDisplayName() { 132 | return "2PivotQk"; 133 | } 134 | 135 | @Override 136 | public void sort(String[] data) { 137 | DualPivotQuicksort.sort(data); 138 | } 139 | }, 140 | SELECTION { 141 | 142 | @Override 143 | public String getDisplayName() { 144 | return "Selectionsort"; 145 | } 146 | 147 | @Override 148 | public void sort(String[] data) { 149 | Selectionsort.sort(data, 0, data.length - 1); 150 | } 151 | }, 152 | SHELL { 153 | 154 | @Override 155 | public String getDisplayName() { 156 | return "Shellsort"; 157 | } 158 | 159 | @Override 160 | public void sort(String[] data) { 161 | Shellsort.sort(data); 162 | } 163 | }, 164 | BURST { 165 | 166 | @Override 167 | public String getDisplayName() { 168 | return "Burstsort"; 169 | } 170 | 171 | @Override 172 | public void sort(String[] data) { 173 | Burstsort.sort(data); 174 | } 175 | }, 176 | REDESIGNED_BURST { 177 | 178 | @Override 179 | public String getDisplayName() { 180 | return "BR-Burstsort"; 181 | } 182 | 183 | @Override 184 | public void sort(String[] data) { 185 | RedesignedBurstsort.sort(data); 186 | } 187 | }, 188 | BURST_THREADPOOL { 189 | 190 | @Override 191 | public String getDisplayName() { 192 | return "Burstsort|TP|"; 193 | } 194 | 195 | @Override 196 | public void sort(String[] data) { 197 | try { 198 | Burstsort.sortThreadPool(data); 199 | } catch (InterruptedException ie) { 200 | throw new RuntimeException(ie); 201 | } 202 | } 203 | }, 204 | REDESIGNED_BURST_THREADPOOL { 205 | 206 | @Override 207 | public String getDisplayName() { 208 | return "BR-Burstsort|TP|"; 209 | } 210 | 211 | @Override 212 | public void sort(String[] data) { 213 | try { 214 | RedesignedBurstsort.sortThreadPool(data); 215 | } catch (InterruptedException ie) { 216 | throw new RuntimeException(ie); 217 | } 218 | } 219 | }, 220 | LAZY_FUNNEL { 221 | 222 | @Override 223 | public String getDisplayName() { 224 | return "LazyFunnelsort"; 225 | } 226 | 227 | @Override 228 | public void sort(String[] data) { 229 | LazyFunnelsort.sort(data); 230 | } 231 | }, 232 | THREADED_LAZY_FUNNEL { 233 | 234 | @Override 235 | public String getDisplayName() { 236 | return "LazyFunnelsort|TP|"; 237 | } 238 | 239 | @Override 240 | public void sort(String[] data) { 241 | LazyFunnelsort.sortThreaded(data); 242 | } 243 | }, 244 | MERGE { 245 | 246 | @Override 247 | public String getDisplayName() { 248 | return "Mergesort"; 249 | } 250 | 251 | @Override 252 | public void sort(String[] data) { 253 | // This uses a merge sort. 254 | Arrays.sort(data); 255 | } 256 | }, 257 | MULTIKEY { 258 | 259 | @Override 260 | public String getDisplayName() { 261 | return "MultikeyQS"; 262 | } 263 | 264 | @Override 265 | public void sort(String[] data) { 266 | MultikeyQuicksort.sort(data); 267 | } 268 | }; 269 | 270 | /** 271 | * Returns the display name for this runner. 272 | * 273 | * @return display name. 274 | */ 275 | public abstract String getDisplayName(); 276 | 277 | /** 278 | * Sort the given array of strings. 279 | * 280 | * @param data strings to be sorted. 281 | */ 282 | public abstract void sort(String[] data); 283 | } 284 | -------------------------------------------------------------------------------- /src/org/burstsort4j/MultikeyQuicksort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * A Java implementation of multikey quicksort, translated from the 10 | * original C implementation by J. Bentley and R. Sedgewick, from 11 | * their "Fast algorithms for sorting and searching strings" paper 12 | * published in 1997. 13 | * 14 | * @author Nathan Fiedler 15 | */ 16 | public class MultikeyQuicksort { 17 | 18 | /** As with GCC std::sort delegate to insertion sort for ranges of 19 | * size below 16. */ 20 | private static final int THRESHOLD = 16; 21 | 22 | /** 23 | * Creates a new instance of MultikeyQuicksort. 24 | */ 25 | private MultikeyQuicksort() { 26 | } 27 | 28 | /** 29 | * Retrieve the character in string s at offset d. If d is greater 30 | * than or equal to the length of the string, return zero. This 31 | * simulates fixed-length strings that are zero-padded. 32 | * 33 | * @param s string. 34 | * @param d offset. 35 | * @return character in s at d, or zero. 36 | */ 37 | private static char charAt(CharSequence s, int d) { 38 | return d < s.length() ? s.charAt(d) : 0; 39 | } 40 | 41 | /** 42 | * Swap the elements between to subarrays. 43 | * 44 | * @param a the array of elements. 45 | * @param i offset of first subarray. 46 | * @param j offset of second subarray. 47 | * @param n number of elements to swap. 48 | */ 49 | private static void vecswap(Object[] a, int i, int j, int n) { 50 | while (n-- > 0) { 51 | Object t = a[i]; 52 | a[i] = a[j]; 53 | a[j] = t; 54 | i++; 55 | j++; 56 | } 57 | } 58 | 59 | /** 60 | * Sorts the array of strings using a multikey quicksort that chooses 61 | * a pivot point using a "median of three" rule (or pseudo median of 62 | * nine for arrays over a certain threshold). For very small subarrays, 63 | * an insertion sort is used. 64 | * 65 | * @param strings array of strings to be sorted. 66 | */ 67 | public static void sort(CharSequence[] strings) { 68 | if (strings != null && strings.length > 1) { 69 | ssort(strings, 0, strings.length, 0); 70 | } 71 | } 72 | 73 | /** 74 | * Sorts the array of strings using a multikey quicksort that chooses 75 | * a pivot point using a "median of three" rule (or pseudo median of 76 | * nine for arrays over a certain threshold). For very small subarrays, 77 | * an insertion sort is used. 78 | * 79 | *

Only characters in the strings starting from the given offset 80 | * depth are considered. That is, the method will ignore all 81 | * characters appearing before the depth character.

82 | * 83 | * @param strings array of strings to sort. 84 | * @param low low offset into the array (inclusive). 85 | * @param high high offset into the array (exclusive). 86 | * @param depth offset of first character in each string to compare. 87 | */ 88 | public static void sort(CharSequence[] strings, int low, int high, int depth) { 89 | if (strings != null && strings.length > 1 && low >= 0 && low < high && depth >= 0) { 90 | ssort(strings, low, high - low, depth); 91 | } 92 | } 93 | 94 | /** 95 | * Find the median of three characters, found in the given strings 96 | * at character position depth. One of the three integer 97 | * values will be returned based on the comparisons. 98 | * 99 | * @param a array of strings. 100 | * @param l low index. 101 | * @param m middle index. 102 | * @param h high index. 103 | * @param depth character offset. 104 | * @return the position of the median string. 105 | */ 106 | private static int med3(CharSequence[] a, int l, int m, int h, int depth) { 107 | char va = charAt(a[l], depth); 108 | char vb = charAt(a[m], depth); 109 | if (va == vb) { 110 | return l; 111 | } 112 | char vc = charAt(a[h], depth); 113 | if (vc == va || vc == vb) { 114 | return h; 115 | } 116 | return va < vb ? (vb < vc ? m : (va < vc ? h : l)) 117 | : (vb > vc ? m : (va < vc ? l : h)); 118 | } 119 | 120 | /** 121 | * The recursive portion of multikey quicksort. 122 | * 123 | * @param strings the array of strings to sort. 124 | * @param base zero-based offset into array to be considered. 125 | * @param length length of subarray to consider. 126 | * @param depth the zero-based offset into the strings. 127 | */ 128 | private static void ssort(CharSequence[] a, int base, int n, int depth) { 129 | if (n < THRESHOLD) { 130 | Insertionsort.sort(a, base, base + n, depth); 131 | return; 132 | } 133 | int pl = base; 134 | int pm = base + n / 2; 135 | int pn = base + n - 1; 136 | int r; 137 | if (n > 30) { 138 | // On larger arrays, find a pseudo median of nine elements. 139 | int d = n / 8; 140 | pl = med3(a, base, base + d, base + 2 * d, depth); 141 | pm = med3(a, base + n / 2 - d, pm, base + n / 2 + d, depth); 142 | pn = med3(a, base + n - 1 - 2 * d, base + n - 1 - d, pn, depth); 143 | } 144 | pm = med3(a, pl, pm, pn, depth); 145 | CharSequence t = a[base]; 146 | a[base] = a[pm]; 147 | a[pm] = t; 148 | int v = charAt(a[base], depth); 149 | boolean allzeros = v == 0; 150 | int le = base + 1, lt = le; 151 | int gt = base + n - 1, ge = gt; 152 | while (true) { 153 | for (; lt <= gt && (r = charAt(a[lt], depth) - v) <= 0; lt++) { 154 | if (r == 0) { 155 | t = a[le]; 156 | a[le] = a[lt]; 157 | a[lt] = t; 158 | le++; 159 | } else { 160 | allzeros = false; 161 | } 162 | } 163 | for (; lt <= gt && (r = charAt(a[gt], depth) - v) >= 0; gt--) { 164 | if (r == 0) { 165 | t = a[gt]; 166 | a[gt] = a[ge]; 167 | a[ge] = t; 168 | ge--; 169 | } else { 170 | allzeros = false; 171 | } 172 | } 173 | if (lt > gt) { 174 | break; 175 | } 176 | t = a[lt]; 177 | a[lt] = a[gt]; 178 | a[gt] = t; 179 | lt++; 180 | gt--; 181 | } 182 | pn = base + n; 183 | r = Math.min(le - base, lt - le); 184 | vecswap(a, base, lt - r, r); 185 | r = Math.min(ge - gt, pn - ge - 1); 186 | vecswap(a, lt, pn - r, r); 187 | if ((r = lt - le) > 1) { 188 | ssort(a, base, r, depth); 189 | } 190 | if (!allzeros) { 191 | // Only descend if there was at least one string that was 192 | // of equal or greater length than current depth. 193 | ssort(a, base + r, le + n - ge - 1, depth + 1); 194 | } 195 | if ((r = ge - gt) > 1) { 196 | ssort(a, base + n - r, r, depth); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /test/org/burstsort4j/FunnelsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Funnelsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class FunnelsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Funnelsort.sort((String[]) null); 26 | Funnelsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Funnelsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Funnelsort.sort(arr); 31 | assertEquals("a", arr[0]); 32 | assertEquals("b", arr[1]); 33 | arr = new String[]{"c", "b", "a"}; 34 | Funnelsort.sort(arr); 35 | assertEquals("a", arr[0]); 36 | assertEquals("b", arr[1]); 37 | assertEquals("c", arr[2]); 38 | arr = new String[]{"c", "d", "b", "e", "a"}; 39 | Funnelsort.sort(arr); 40 | assertEquals("a", arr[0]); 41 | assertEquals("b", arr[1]); 42 | assertEquals("c", arr[2]); 43 | assertEquals("d", arr[3]); 44 | assertEquals("e", arr[4]); 45 | arr = new String[]{"j", "f", "c", "b", "i", "g", "a", "d", "e", "h"}; 46 | Funnelsort.sort(arr); 47 | assertEquals("a", arr[0]); 48 | assertEquals("b", arr[1]); 49 | assertEquals("c", arr[2]); 50 | assertEquals("d", arr[3]); 51 | assertEquals("e", arr[4]); 52 | assertEquals("f", arr[5]); 53 | assertEquals("g", arr[6]); 54 | assertEquals("h", arr[7]); 55 | assertEquals("i", arr[8]); 56 | assertEquals("j", arr[9]); 57 | // test with all empty input 58 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 59 | Funnelsort.sort(arr); 60 | for (String s : arr) { 61 | assertEquals("", s); 62 | } 63 | // test with peculiar input 64 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 65 | Funnelsort.sort(arr); 66 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 67 | } 68 | 69 | // @Test 70 | public void testSmallReversed() { 71 | try { 72 | List data = Tests.loadData(); 73 | data = data.subList(0, 1024); 74 | Collections.reverse(data); 75 | String[] arr = data.toArray(new String[data.size()]); 76 | Funnelsort.sort(arr); 77 | assertTrue(Tests.isSorted(arr)); 78 | } catch (IOException ioe) { 79 | fail(ioe.toString()); 80 | } 81 | } 82 | 83 | // @Test 84 | public void testTinyShuffled() { 85 | try { 86 | List data = Tests.loadData(); 87 | Collections.shuffle(data); 88 | data = data.subList(0, 100); 89 | String[] arr = data.toArray(new String[data.size()]); 90 | Funnelsort.sort(arr); 91 | assertTrue(Tests.isSorted(arr)); 92 | } catch (IOException ioe) { 93 | fail(ioe.toString()); 94 | } 95 | } 96 | 97 | // @Test 98 | public void testSmallShuffled() { 99 | try { 100 | List data = Tests.loadData(); 101 | Collections.shuffle(data); 102 | data = data.subList(0, 1024); 103 | String[] arr = data.toArray(new String[data.size()]); 104 | Funnelsort.sort(arr); 105 | assertTrue(Tests.isSorted(arr)); 106 | } catch (IOException ioe) { 107 | fail(ioe.toString()); 108 | } 109 | } 110 | 111 | // @Test 112 | public void testDictWords() { 113 | try { 114 | // Use the large dictionary rather than the trivial one. 115 | List data = Tests.loadData("dictwords.gz", true); 116 | Collections.shuffle(data); 117 | String[] arr = data.toArray(new String[data.size()]); 118 | Funnelsort.sort(arr); 119 | assertTrue(Tests.isSorted(arr)); 120 | } catch (IOException ioe) { 121 | fail(ioe.toString()); 122 | } 123 | } 124 | 125 | // @Test 126 | public void testSorted() { 127 | try { 128 | List data = Tests.loadData(); 129 | Collections.sort(data); 130 | String[] arr = data.toArray(new String[data.size()]); 131 | Funnelsort.sort(arr); 132 | assertTrue(Tests.isSorted(arr)); 133 | } catch (IOException ioe) { 134 | fail(ioe.toString()); 135 | } 136 | } 137 | 138 | // @Test 139 | public void testReversed() { 140 | try { 141 | List data = Tests.loadData(); 142 | Collections.sort(data); 143 | Collections.reverse(data); 144 | String[] arr = data.toArray(new String[data.size()]); 145 | Funnelsort.sort(arr); 146 | assertTrue(Tests.isSorted(arr)); 147 | } catch (IOException ioe) { 148 | fail(ioe.toString()); 149 | } 150 | } 151 | 152 | // @Test 153 | public void testRepeated() { 154 | String[] arr = new String[1310720]; 155 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 156 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 157 | Arrays.fill(arr, STR); 158 | Funnelsort.sort(arr); 159 | assertTrue(Tests.isRepeated(arr, STR)); 160 | } 161 | 162 | // @Test 163 | public void testRepeatedCycle() { 164 | String[] strs = new String[100]; 165 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 166 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 167 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 168 | strs[i] = seed.substring(0, l); 169 | } 170 | List list = new ArrayList(); 171 | for (int c = 3162300, i = 0; c > 0; i++, c--) { 172 | list.add(strs[i % strs.length]); 173 | } 174 | String[] arr = list.toArray(new String[list.size()]); 175 | Funnelsort.sort(arr); 176 | assertTrue(Tests.isSorted(arr)); 177 | } 178 | 179 | // @Test 180 | public void testRandom() { 181 | List data = Tests.generateData(1000000, 100); 182 | String[] arr = data.toArray(new String[data.size()]); 183 | Funnelsort.sort(arr); 184 | assertTrue(Tests.isSorted(arr)); 185 | } 186 | 187 | // @Test 188 | public void testHamlet() { 189 | try { 190 | List data = Tests.loadData("hamletwords"); 191 | Collections.shuffle(data); 192 | String[] arr = data.toArray(new String[data.size()]); 193 | Funnelsort.sort(arr); 194 | assertTrue(Tests.isSorted(arr)); 195 | } catch (IOException ioe) { 196 | fail(ioe.toString()); 197 | } 198 | } 199 | 200 | // @Test 201 | public void testDictCalls() { 202 | try { 203 | List data = Tests.loadData("dictcalls.gz", true); 204 | String[] arr = data.toArray(new String[data.size()]); 205 | Funnelsort.sort(arr); 206 | assertTrue(Tests.isSorted(arr)); 207 | } catch (IOException ioe) { 208 | fail(ioe.toString()); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /test/org/burstsort4j/BurstsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the Burstsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class BurstsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | Burstsort.sort(null); 26 | Burstsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | Burstsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | Burstsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | Burstsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | Burstsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | Burstsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | // Use the large dictionary rather than the trivial one. 51 | List data = Tests.loadData("dictwords.gz", true); 52 | Collections.shuffle(data); 53 | String[] arr = data.toArray(new String[data.size()]); 54 | System.out.format("\nDictionary words (large):\n"); 55 | Burstsort.sort(arr, System.out); 56 | assertTrue(Tests.isSorted(arr)); 57 | } catch (IOException ioe) { 58 | fail(ioe.toString()); 59 | } 60 | } 61 | 62 | @Test 63 | public void testDictWordsParallel() { 64 | try { 65 | // Use the large dictionary rather than the trivial one. 66 | List data = Tests.loadData("dictwords.gz", true); 67 | Collections.shuffle(data); 68 | String[] arr = data.toArray(new String[data.size()]); 69 | try { 70 | Burstsort.sortThreadPool(arr); 71 | } catch (InterruptedException ie) { 72 | fail(ie.toString()); 73 | } 74 | assertTrue(Tests.isSorted(arr)); 75 | } catch (IOException ioe) { 76 | fail(ioe.toString()); 77 | } 78 | } 79 | 80 | @Test 81 | public void testSorted() { 82 | try { 83 | List data = Tests.loadData(); 84 | Collections.sort(data); 85 | String[] arr = data.toArray(new String[data.size()]); 86 | System.out.format("\nDictionary words (sorted):\n"); 87 | Burstsort.sort(arr, System.out); 88 | assertTrue(Tests.isSorted(arr)); 89 | } catch (IOException ioe) { 90 | fail(ioe.toString()); 91 | } 92 | } 93 | 94 | @Test 95 | public void testReversed() { 96 | try { 97 | List data = Tests.loadData(); 98 | Collections.sort(data); 99 | Collections.reverse(data); 100 | String[] arr = data.toArray(new String[data.size()]); 101 | System.out.format("\nDictionary words (reversed):\n"); 102 | Burstsort.sort(arr, System.out); 103 | assertTrue(Tests.isSorted(arr)); 104 | } catch (IOException ioe) { 105 | fail(ioe.toString()); 106 | } 107 | } 108 | 109 | @Test 110 | public void testRepeated() { 111 | // Make the size of the set large enough to burst buckets. 112 | String[] arr = new String[1310720]; 113 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 114 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 115 | Arrays.fill(arr, STR); 116 | System.out.format("\nRepeated 100-A string:\n"); 117 | Burstsort.sort(arr, System.out); 118 | assertTrue(Tests.isRepeated(arr, STR)); 119 | } 120 | 121 | @Test 122 | public void testRepeatedParallel() { 123 | // Make the size of the set large enough to burst buckets. 124 | String[] arr = new String[1310720]; 125 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 126 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 127 | Arrays.fill(arr, STR); 128 | try { 129 | Burstsort.sortThreadPool(arr); 130 | } catch (InterruptedException ie) { 131 | fail(ie.toString()); 132 | } 133 | assertTrue(Tests.isRepeated(arr, STR)); 134 | } 135 | 136 | @Test 137 | public void testRepeatedCycle() { 138 | String[] strs = new String[100]; 139 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 140 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 141 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 142 | strs[i] = seed.substring(0, l); 143 | } 144 | List list = new ArrayList(); 145 | for (int c = 3162300, i = 0; c > 0; i++, c--) { 146 | list.add(strs[i % strs.length]); 147 | } 148 | System.out.format("\nRepeated A strings (cycle):\n"); 149 | String[] arr = list.toArray(new String[list.size()]); 150 | Burstsort.sort(arr, System.out); 151 | assertTrue(Tests.isSorted(arr)); 152 | } 153 | 154 | @Test 155 | public void testRepeatedCycleParallel() { 156 | String[] strs = new String[100]; 157 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 158 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 159 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 160 | strs[i] = seed.substring(0, l); 161 | } 162 | List list = new ArrayList(); 163 | for (int c = 3162300, i = 0; c > 0; i++, c--) { 164 | list.add(strs[i % strs.length]); 165 | } 166 | String[] arr = list.toArray(new String[list.size()]); 167 | try { 168 | Burstsort.sortThreadPool(arr); 169 | } catch (InterruptedException ie) { 170 | fail(ie.toString()); 171 | } 172 | assertTrue(Tests.isSorted(arr)); 173 | } 174 | 175 | @Test 176 | public void testRandom() { 177 | List data = Tests.generateData(1000000, 100); 178 | String[] arr = data.toArray(new String[data.size()]); 179 | System.out.format("\nRandom strings:\n"); 180 | Burstsort.sort(arr, System.out); 181 | assertTrue(Tests.isSorted(arr)); 182 | } 183 | 184 | @Test 185 | public void testHamlet() { 186 | try { 187 | List data = Tests.loadData("hamletwords"); 188 | Collections.shuffle(data); 189 | String[] arr = data.toArray(new String[data.size()]); 190 | System.out.format("\nHamlet words:\n"); 191 | Burstsort.sort(arr, System.out); 192 | assertTrue(Tests.isSorted(arr)); 193 | } catch (IOException ioe) { 194 | fail(ioe.toString()); 195 | } 196 | } 197 | 198 | @Test 199 | public void testDictCalls() { 200 | try { 201 | List data = Tests.loadData("dictcalls.gz", true); 202 | String[] arr = data.toArray(new String[data.size()]); 203 | System.out.format("\nLibrary calls:\n"); 204 | Burstsort.sort(arr, System.out); 205 | assertTrue(Tests.isSorted(arr)); 206 | } catch (IOException ioe) { 207 | fail(ioe.toString()); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/org/burstsort4j/Introsort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | /** 9 | * Implementation of the introspective sort algorithm, developed by 10 | * David Musser; implementation copied from the paper on introsort 11 | * by Ralph Unden, with some modifications. 12 | * 13 | * @author Nathan Fiedler 14 | */ 15 | public class Introsort { 16 | 17 | /** As with typical quicksort implementations, delegate to insertion 18 | * sort for ranges of size below 16. */ 19 | private static final int THRESHOLD = 16; 20 | 21 | private Introsort() { 22 | } 23 | 24 | /** 25 | * Sort the array of comparables. Uses an introspective sort 26 | * algorithm, so expect O(log(n)) running time. 27 | * 28 | * @param type of comparable to be sorted. 29 | * @param arr comparables to be sorted. 30 | */ 31 | public static > void sort(T[] arr) { 32 | if (arr != null && arr.length > 1) { 33 | int floor = (int) (Math.floor(Math.log(arr.length) / Math.log(2))); 34 | innerLoop(0, arr.length, 2 * floor, arr); 35 | insertionsort(0, arr.length, arr); 36 | } 37 | } 38 | 39 | /** 40 | * Sort the array of comparables within the given range of elements. 41 | * Uses an introspective sort algorithm, so expect O(log(n)) running 42 | * time. 43 | * 44 | * @param type of comparable to be sorted. 45 | * @param arr comparables to be sorted. 46 | * @param low low end of range to sort (inclusive). 47 | * @param high high end of range to sort (inclusive). 48 | */ 49 | public static > void sort(T[] arr, int low, int high) { 50 | if (arr != null && arr.length > 1 && low >= 0 && low < high) { 51 | int floor = (int) (Math.floor(Math.log(high - low) / Math.log(2))); 52 | innerLoop(low, high, 2 * floor, arr); 53 | insertionsort(low, high, arr); 54 | } 55 | } 56 | 57 | /** 58 | * A modified quicksort that delegates to heapsort when the depth 59 | * limit has been reached. Does not sort the array if the range is 60 | * below the threshold. 61 | * 62 | * @param type of comparable to be sorted. 63 | * @param arr comparables to be sorted. 64 | * @param low low end of range to sort (inclusive). 65 | * @param high high end of range to sort (inclusive). 66 | * @param depth_limit if zero, will delegate to heapsort. 67 | */ 68 | private static > void innerLoop( 69 | int low, int high, int depth_limit, T[] arr) { 70 | while (high - low > THRESHOLD) { 71 | if (depth_limit == 0) { 72 | // perform a basic heap sort 73 | int n = high - low; 74 | for (int i = n / 2; i >= 1; i--) { 75 | T d = arr[low + i - 1]; 76 | int j = i; 77 | while (j <= n / 2) { 78 | int child = 2 * j; 79 | if (child < n && arr[low + child - 1].compareTo(arr[low + child]) < 0) { 80 | child++; 81 | } 82 | if (d.compareTo(arr[low + child - 1]) >= 0) { 83 | break; 84 | } 85 | arr[low + j - 1] = arr[low + child - 1]; 86 | j = child; 87 | } 88 | arr[low + j - 1] = d; 89 | } 90 | for (int i = n; i > 1; i--) { 91 | T t = arr[low]; 92 | arr[low] = arr[low + i - 1]; 93 | arr[low + i - 1] = t; 94 | T d = arr[low + i - 1]; 95 | int j = 1; 96 | int m = i - 1; 97 | while (j <= m / 2) { 98 | int child = 2 * j; 99 | if (child < m && arr[low + child - 1].compareTo(arr[low + child]) < 0) { 100 | child++; 101 | } 102 | if (d.compareTo(arr[low + child - 1]) >= 0) { 103 | break; 104 | } 105 | arr[low + j - 1] = arr[low + child - 1]; 106 | j = child; 107 | } 108 | arr[low + j - 1] = d; 109 | } 110 | return; 111 | } 112 | depth_limit--; 113 | int p = partition(low, high, medianOf3(low, low + ((high - low) / 2) + 1, high - 1, arr), arr); 114 | innerLoop(p, high, depth_limit, arr); 115 | high = p; 116 | } 117 | } 118 | 119 | /** 120 | * Partitions the elements in the given range such that elements 121 | * less than the pivot appear before those greater than the pivot. 122 | * 123 | * @param type of comparable to be sorted. 124 | * @param low low end of range to sort (inclusive). 125 | * @param high high end of range to sort (inclusive). 126 | * @param x pivot to compare to. 127 | * @param arr comparables to be sorted. 128 | * @return midpoint of partitioned values. 129 | */ 130 | private static > int partition(int low, int high, T x, T[] arr) { 131 | int i = low; 132 | int j = high; 133 | while (true) { 134 | while (arr[i].compareTo(x) < 0) { 135 | i++; 136 | } 137 | j--; 138 | while (x.compareTo(arr[j]) < 0) { 139 | j--; 140 | } 141 | if (i >= j) { 142 | return i; 143 | } 144 | T t = arr[i]; 145 | arr[i] = arr[j]; 146 | arr[j] = t; 147 | i++; 148 | } 149 | } 150 | 151 | /** 152 | * Finds the median of three element in the given range. 153 | * 154 | * @param type of comparable to be sorted. 155 | * @param low low end of range to sort (inclusive). 156 | * @param mid midpoint of the range. 157 | * @param high high end of range to sort (inclusive). 158 | * @param arr comparables to be sorted. 159 | * @return the median of three element. 160 | */ 161 | private static > T medianOf3(int low, int mid, int high, T[] arr) { 162 | if (arr[mid].compareTo(arr[low]) < 0) { 163 | if (arr[high].compareTo(arr[mid]) < 0) { 164 | return arr[mid]; 165 | } else { 166 | if (arr[high].compareTo(arr[low]) < 0) { 167 | return arr[high]; 168 | } else { 169 | return arr[low]; 170 | } 171 | } 172 | } else { 173 | if (arr[high].compareTo(arr[mid]) < 0) { 174 | if (arr[high].compareTo(arr[low]) < 0) { 175 | return arr[low]; 176 | } else { 177 | return arr[high]; 178 | } 179 | } else { 180 | return arr[mid]; 181 | } 182 | } 183 | } 184 | 185 | /** 186 | * A simple insertion sort that operates on the given range. 187 | * 188 | * @param type of comparable to be sorted. 189 | * @param low low end of range to heapify (inclusive). 190 | * @param high high end of range to sort (inclusive). 191 | * @param arr comparables to be sorted. 192 | */ 193 | private static > void insertionsort(int low, int high, T[] arr) { 194 | for (int i = low; i < high; i++) { 195 | int j = i; 196 | T t = arr[i]; 197 | while (j != low && t.compareTo(arr[j - 1]) < 0) { 198 | arr[j] = arr[j - 1]; 199 | j--; 200 | } 201 | arr[j] = t; 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /test/org/burstsort4j/RedesignedBurstsortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.Test; 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * Unit tests for the RedesignedBurstsort class. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class RedesignedBurstsortTest { 22 | 23 | @Test 24 | public void testArguments() { 25 | RedesignedBurstsort.sort(null); 26 | RedesignedBurstsort.sort(new String[0]); 27 | String[] arr = new String[]{"a"}; 28 | RedesignedBurstsort.sort(arr); 29 | arr = new String[]{"b", "a"}; 30 | RedesignedBurstsort.sort(arr); 31 | assertTrue(Tests.isSorted(arr)); 32 | arr = new String[]{"c", "b", "a"}; 33 | RedesignedBurstsort.sort(arr); 34 | assertTrue(Tests.isSorted(arr)); 35 | // test with all empty input 36 | arr = new String[]{"", "", "", "", "", "", "", "", "", ""}; 37 | RedesignedBurstsort.sort(arr); 38 | for (String s : arr) { 39 | assertEquals("", s); 40 | } 41 | // test with peculiar input 42 | arr = new String[]{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}; 43 | RedesignedBurstsort.sort(arr); 44 | assertTrue("peculiar input not sorted", Tests.isSorted(arr)); 45 | } 46 | 47 | @Test 48 | public void testDictWords() { 49 | try { 50 | // Use the large dictionary rather than the trivial one. 51 | List data = Tests.loadData("dictwords.gz", true); 52 | Collections.shuffle(data); 53 | String[] arr = data.toArray(new String[data.size()]); 54 | System.out.format("\nDictionary words (large):\n"); 55 | RedesignedBurstsort.sort(arr, System.out); 56 | assertTrue(Tests.isSorted(arr)); 57 | } catch (IOException ioe) { 58 | fail(ioe.toString()); 59 | } 60 | } 61 | 62 | @Test 63 | public void testDictWordsParallel() { 64 | try { 65 | // Use the large dictionary rather than the trivial one. 66 | List data = Tests.loadData("dictwords.gz", true); 67 | Collections.shuffle(data); 68 | String[] arr = data.toArray(new String[data.size()]); 69 | try { 70 | RedesignedBurstsort.sortThreadPool(arr); 71 | } catch (InterruptedException ie) { 72 | fail(ie.toString()); 73 | } 74 | assertTrue(Tests.isSorted(arr)); 75 | } catch (IOException ioe) { 76 | fail(ioe.toString()); 77 | } 78 | } 79 | 80 | @Test 81 | public void testSorted() { 82 | try { 83 | List data = Tests.loadData(); 84 | Collections.sort(data); 85 | String[] arr = data.toArray(new String[data.size()]); 86 | System.out.format("\nDictionary words (sorted):\n"); 87 | RedesignedBurstsort.sort(arr, System.out); 88 | assertTrue(Tests.isSorted(arr)); 89 | } catch (IOException ioe) { 90 | fail(ioe.toString()); 91 | } 92 | } 93 | 94 | @Test 95 | public void testReversed() { 96 | try { 97 | List data = Tests.loadData(); 98 | Collections.sort(data); 99 | Collections.reverse(data); 100 | String[] arr = data.toArray(new String[data.size()]); 101 | System.out.format("\nDictionary words (reversed):\n"); 102 | RedesignedBurstsort.sort(arr, System.out); 103 | assertTrue(Tests.isSorted(arr)); 104 | } catch (IOException ioe) { 105 | fail(ioe.toString()); 106 | } 107 | } 108 | 109 | @Test 110 | public void testRepeated() { 111 | // Make the size of the set large enough to burst buckets. 112 | String[] arr = new String[1310720]; 113 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 114 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 115 | Arrays.fill(arr, STR); 116 | System.out.format("\nRepeated 100-A string:\n"); 117 | RedesignedBurstsort.sort(arr, System.out); 118 | assertTrue(Tests.isRepeated(arr, STR)); 119 | } 120 | 121 | @Test 122 | public void testRepeatedParallel() { 123 | // Make the size of the set large enough to burst buckets. 124 | String[] arr = new String[1310720]; 125 | final String STR = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 126 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 127 | Arrays.fill(arr, STR); 128 | try { 129 | RedesignedBurstsort.sortThreadPool(arr); 130 | } catch (InterruptedException ie) { 131 | fail(ie.toString()); 132 | } 133 | assertTrue(Tests.isRepeated(arr, STR)); 134 | } 135 | 136 | @Test 137 | public void testRepeatedCycle() { 138 | String[] strs = new String[100]; 139 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 140 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 141 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 142 | strs[i] = seed.substring(0, l); 143 | } 144 | List list = new ArrayList(); 145 | for (int c = 3162300, i = 0; c > 0; i++, c--) { 146 | list.add(strs[i % strs.length]); 147 | } 148 | System.out.format("\nRepeated A strings (cycle):\n"); 149 | String[] arr = list.toArray(new String[list.size()]); 150 | RedesignedBurstsort.sort(arr, System.out); 151 | assertTrue(Tests.isSorted(arr)); 152 | } 153 | 154 | @Test 155 | public void testRepeatedCycleParallel() { 156 | String[] strs = new String[100]; 157 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 158 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 159 | for (int i = 0, l = 1; i < strs.length; i++, l++) { 160 | strs[i] = seed.substring(0, l); 161 | } 162 | List list = new ArrayList(); 163 | for (int c = 3162300, i = 0; c > 0; i++, c--) { 164 | list.add(strs[i % strs.length]); 165 | } 166 | String[] arr = list.toArray(new String[list.size()]); 167 | try { 168 | RedesignedBurstsort.sortThreadPool(arr); 169 | } catch (InterruptedException ie) { 170 | fail(ie.toString()); 171 | } 172 | assertTrue(Tests.isSorted(arr)); 173 | } 174 | 175 | @Test 176 | public void testRandom() { 177 | List data = Tests.generateData(1000000, 100); 178 | String[] arr = data.toArray(new String[data.size()]); 179 | System.out.format("\nRandom strings:\n"); 180 | RedesignedBurstsort.sort(arr, System.out); 181 | assertTrue(Tests.isSorted(arr)); 182 | } 183 | 184 | @Test 185 | public void testHamlet() { 186 | try { 187 | List data = Tests.loadData("hamletwords"); 188 | Collections.shuffle(data); 189 | String[] arr = data.toArray(new String[data.size()]); 190 | System.out.format("\nHamlet words:\n"); 191 | RedesignedBurstsort.sort(arr, System.out); 192 | assertTrue(Tests.isSorted(arr)); 193 | } catch (IOException ioe) { 194 | fail(ioe.toString()); 195 | } 196 | } 197 | 198 | @Test 199 | public void testDictCalls() { 200 | try { 201 | List data = Tests.loadData("dictcalls.gz", true); 202 | String[] arr = data.toArray(new String[data.size()]); 203 | System.out.format("\nLibrary calls:\n"); 204 | RedesignedBurstsort.sort(arr, System.out); 205 | assertTrue(Tests.isSorted(arr)); 206 | } catch (IOException ioe) { 207 | fail(ioe.toString()); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/MacroBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import java.util.ArrayList; 9 | import java.util.EnumMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * Runs performance tests over several kinds of data for each of the 17 | * sort implementations, collecting run times and displaying the results. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class MacroBenchmark { 22 | 23 | /** 24 | * Creates a new instance of MacroBenchmark. 25 | */ 26 | private MacroBenchmark() { 27 | } 28 | 29 | /** 30 | * Command-line interface to benchmark driver. 31 | * 32 | * @param args command-line arguments. 33 | */ 34 | public static void main(String[] args) { 35 | DataGenerator[] generators = { 36 | DataGenerator.RANDOM, 37 | DataGenerator.REPEAT, 38 | DataGenerator.REPEAT_CYCLE, 39 | DataGenerator.PSEUDO_WORD, 40 | DataGenerator.GENOME, 41 | DataGenerator.SMALL_ALPHABET, 42 | DataGenerator.MEDIAN_OF_3_KILLER 43 | }; 44 | // limit to those that perform reasonably well with larger data sets 45 | SortRunner[] runners = { 46 | SortRunner.HEAP, 47 | SortRunner.INTRO, 48 | SortRunner.QUICK, 49 | SortRunner.QUICK_2_PIVOT, 50 | SortRunner.MULTIKEY 51 | }; 52 | // use data sizes larger than micro but not huge 53 | DataSize[] sizes = { 54 | DataSize.N_1000, 55 | DataSize.N_4000, 56 | DataSize.N_16000, 57 | DataSize.N_64000, 58 | DataSize.N_256000 59 | }; 60 | 61 | if (args.length > 0) { 62 | // Parse the command line arguments. 63 | int i = 0; 64 | while (i < args.length) { 65 | if (args[i].equals("--data")) { 66 | i++; 67 | if (i >= args.length) { 68 | usage("Missing --data argument"); 69 | } 70 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 71 | List list = new ArrayList(); 72 | for (DataGenerator generator : generators) { 73 | Matcher m = p.matcher(generator.getDisplayName()); 74 | if (m.find()) { 75 | list.add(generator); 76 | } 77 | } 78 | generators = list.toArray(new DataGenerator[list.size()]); 79 | } else if (args[i].equals("--sort")) { 80 | i++; 81 | if (i >= args.length) { 82 | usage("Missing --sort argument"); 83 | } 84 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 85 | List list = new ArrayList(); 86 | for (SortRunner runner : runners) { 87 | Matcher m = p.matcher(runner.getDisplayName()); 88 | if (m.find()) { 89 | list.add(runner); 90 | } 91 | } 92 | runners = list.toArray(new SortRunner[list.size()]); 93 | } else if (args[i].equals("--list")) { 94 | System.out.println("Data sets"); 95 | for (DataGenerator generator : generators) { 96 | System.out.format("\t%s\n", generator.getDisplayName()); 97 | } 98 | System.out.println("Sorting algorithms"); 99 | for (SortRunner runner : runners) { 100 | System.out.format("\t%s\n", runner.getDisplayName()); 101 | } 102 | System.exit(0); 103 | } else if (args[i].equals("--help")) { 104 | usage(); 105 | } else { 106 | usage("Unrecognized option: " + args[i]); 107 | } 108 | i++; 109 | } 110 | } 111 | 112 | // Warm up the JVM so that the code (hopefully) gets compiled. 113 | System.out.println("Warming up the system, please wait..."); 114 | for (DataGenerator generator : generators) { 115 | String[] dataSet = generator.generate(DataSize.N_400); 116 | String[] input = new String[dataSet.length]; 117 | for (SortRunner runner : runners) { 118 | for (int i = 0; i < 1000; i++) { 119 | System.arraycopy(dataSet, 0, input, 0, input.length); 120 | runner.sort(input); 121 | } 122 | } 123 | } 124 | 125 | // Avoid recreating the input arrays over and over again. 126 | Map inputSets = new EnumMap(DataSize.class); 127 | for (DataSize size : DataSize.values()) { 128 | inputSets.put(size, new String[size.getValue()]); 129 | } 130 | 131 | // For each type of data set, and each data set size, and 132 | // each sort implementation, run the sort many times and 133 | // calculate an average. 134 | for (DataGenerator generator : generators) { 135 | System.out.format("%s...\n", generator.getDisplayName()); 136 | for (DataSize size : sizes) { 137 | // Must generate the data for each size since some 138 | // generators use it to form a pattern. 139 | final String[] dataSet = generator.generate(size); 140 | System.out.format("\t%s...\n", size.toString()); 141 | final String[] input = inputSets.get(size); 142 | for (final SortRunner runner : runners) { 143 | System.out.format("\t\t%-20s:\t", runner.getDisplayName()); 144 | final SortRunner func = runner; 145 | BenchmarkRunnable r = new BenchmarkRunnable() { 146 | 147 | @Override 148 | public void run(BenchmarkData b) { 149 | for (int i = 0; i < b.count(); i++) { 150 | b.stopTimer(); 151 | System.arraycopy(dataSet, 0, input, 0, input.length); 152 | b.startTimer(); 153 | func.sort(input); 154 | } 155 | } 156 | }; 157 | BenchmarkData bench = new BenchmarkData(r); 158 | BenchmarkResult result = bench.run(); 159 | System.out.format("%8d\t%10d ns/op\n", result.count(), result.nsPerOp()); 160 | } 161 | } 162 | } 163 | } 164 | 165 | /** 166 | * Display an error message and the usage information. 167 | */ 168 | private static void usage(String msg) { 169 | System.out.println(msg); 170 | usage(); 171 | } 172 | 173 | /** 174 | * Display the usage information. 175 | */ 176 | private static void usage() { 177 | System.out.println("Usage: MacroBenchmark [options]"); 178 | System.out.println("\t--data "); 179 | System.out.println("\t\tSelect the data set whose name matches the regular expression."); 180 | System.out.println("\t\tFor example, '--data random' would use only the random data set."); 181 | System.out.println("\t--help"); 182 | System.out.println("\t\tDisplay this usage information."); 183 | System.out.println("\t--list"); 184 | System.out.println("\t\tDisplay a list of the supported data sets and sorting algorithms."); 185 | System.out.println("\t--sort "); 186 | System.out.println("\t\tSelect the sort algorithms whose name matches the regular"); 187 | System.out.println("\t\texpression. For example, '--sort (comb|insert)' would run"); 188 | System.out.println("\t\tboth versions of the insertion and comb sort algorithms."); 189 | System.exit(0); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/MicroBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import java.util.ArrayList; 9 | import java.util.EnumMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * Runs performance tests over several kinds of data for each of the 17 | * sort implementations, collecting run times and displaying the results. 18 | * 19 | * @author Nathan Fiedler 20 | */ 21 | public class MicroBenchmark { 22 | 23 | /** 24 | * Creates a new instance of MicroBenchmark. 25 | */ 26 | private MicroBenchmark() { 27 | } 28 | 29 | /** 30 | * Command-line interface to benchmark driver. 31 | * 32 | * @param args command-line arguments. 33 | */ 34 | public static void main(String[] args) { 35 | DataGenerator[] generators = { 36 | DataGenerator.RANDOM, 37 | DataGenerator.REPEAT, 38 | DataGenerator.REPEAT_CYCLE, 39 | DataGenerator.PSEUDO_WORD, 40 | DataGenerator.GENOME, 41 | DataGenerator.SMALL_ALPHABET, 42 | DataGenerator.MEDIAN_OF_3_KILLER 43 | }; 44 | SortRunner[] runners = { 45 | SortRunner.BINARY_INSERTION, 46 | SortRunner.INSERTION, 47 | SortRunner.COMB, 48 | SortRunner.HYBRID_COMB, 49 | SortRunner.GNOME, 50 | SortRunner.HEAP, 51 | SortRunner.INTRO, 52 | SortRunner.QUICK, 53 | SortRunner.QUICK_2_PIVOT, 54 | SortRunner.SELECTION, 55 | SortRunner.SHELL 56 | }; 57 | DataSize[] sizes = { 58 | DataSize.N_12, 59 | DataSize.N_20, 60 | DataSize.N_52, 61 | DataSize.N_100, 62 | DataSize.N_400, 63 | DataSize.N_800 64 | }; 65 | 66 | if (args.length > 0) { 67 | // Parse the command line arguments. 68 | int i = 0; 69 | while (i < args.length) { 70 | if (args[i].equals("--data")) { 71 | i++; 72 | if (i >= args.length) { 73 | usage("Missing --data argument"); 74 | } 75 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 76 | List list = new ArrayList(); 77 | for (DataGenerator generator : generators) { 78 | Matcher m = p.matcher(generator.getDisplayName()); 79 | if (m.find()) { 80 | list.add(generator); 81 | } 82 | } 83 | generators = list.toArray(new DataGenerator[list.size()]); 84 | } else if (args[i].equals("--sort")) { 85 | i++; 86 | if (i >= args.length) { 87 | usage("Missing --sort argument"); 88 | } 89 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 90 | List list = new ArrayList(); 91 | for (SortRunner runner : runners) { 92 | Matcher m = p.matcher(runner.getDisplayName()); 93 | if (m.find()) { 94 | list.add(runner); 95 | } 96 | } 97 | runners = list.toArray(new SortRunner[list.size()]); 98 | } else if (args[i].equals("--list")) { 99 | System.out.println("Data sets"); 100 | for (DataGenerator generator : generators) { 101 | System.out.format("\t%s\n", generator.getDisplayName()); 102 | } 103 | System.out.println("Sorting algorithms"); 104 | for (SortRunner runner : runners) { 105 | System.out.format("\t%s\n", runner.getDisplayName()); 106 | } 107 | System.exit(0); 108 | } else if (args[i].equals("--help")) { 109 | usage(); 110 | } else { 111 | usage("Unrecognized option: " + args[i]); 112 | } 113 | i++; 114 | } 115 | } 116 | 117 | // Warm up the JVM so that the code (hopefully) gets compiled. 118 | System.out.println("Warming up the system, please wait..."); 119 | for (DataGenerator generator : generators) { 120 | String[] dataSet = generator.generate(DataSize.N_400); 121 | String[] input = new String[dataSet.length]; 122 | for (SortRunner runner : runners) { 123 | for (int i = 0; i < 1000; i++) { 124 | System.arraycopy(dataSet, 0, input, 0, input.length); 125 | runner.sort(input); 126 | } 127 | } 128 | } 129 | 130 | // Avoid recreating the input arrays over and over again. 131 | Map inputSets = new EnumMap(DataSize.class); 132 | for (DataSize size : DataSize.values()) { 133 | inputSets.put(size, new String[size.getValue()]); 134 | } 135 | 136 | // For each type of data set, and each data set size, and 137 | // each sort implementation, run the sort many times and 138 | // calculate an average. 139 | for (DataGenerator generator : generators) { 140 | System.out.format("%s...\n", generator.getDisplayName()); 141 | for (DataSize size : sizes) { 142 | // Must generate the data for each size since some 143 | // generators use it to form a pattern. 144 | final String[] dataSet = generator.generate(size); 145 | System.out.format("\t%s...\n", size.toString()); 146 | final String[] input = inputSets.get(size); 147 | for (final SortRunner runner : runners) { 148 | System.out.format("\t\t%-20s:\t", runner.getDisplayName()); 149 | final SortRunner func = runner; 150 | BenchmarkRunnable r = new BenchmarkRunnable() { 151 | 152 | @Override 153 | public void run(BenchmarkData b) { 154 | for (int i = 0; i < b.count(); i++) { 155 | b.stopTimer(); 156 | System.arraycopy(dataSet, 0, input, 0, input.length); 157 | b.startTimer(); 158 | func.sort(input); 159 | } 160 | } 161 | }; 162 | BenchmarkData bench = new BenchmarkData(r); 163 | BenchmarkResult result = bench.run(); 164 | System.out.format("%8d\t%10d ns/op\n", result.count(), result.nsPerOp()); 165 | } 166 | } 167 | } 168 | } 169 | 170 | /** 171 | * Display an error message and the usage information. 172 | */ 173 | private static void usage(String msg) { 174 | System.out.println(msg); 175 | usage(); 176 | } 177 | 178 | /** 179 | * Display the usage information. 180 | */ 181 | private static void usage() { 182 | System.out.println("Usage: MicroBenchmark [options]"); 183 | System.out.println("\t--data "); 184 | System.out.println("\t\tSelect the data set whose name matches the regular expression."); 185 | System.out.println("\t\tFor example, '--data random' would use only the random data set."); 186 | System.out.println("\t--help"); 187 | System.out.println("\t\tDisplay this usage information."); 188 | System.out.println("\t--list"); 189 | System.out.println("\t\tDisplay a list of the supported data sets and sorting algorithms."); 190 | System.out.println("\t--sort "); 191 | System.out.println("\t\tSelect the sort algorithms whose name matches the regular"); 192 | System.out.println("\t\texpression. For example, '--sort (comb|insert)' would run"); 193 | System.out.println("\t\tboth versions of the insertion and comb sort algorithms."); 194 | System.exit(0); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/DataGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.File; 10 | import java.io.FileReader; 11 | import java.io.IOException; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.Random; 15 | import org.burstsort4j.DualPivotQuicksort; 16 | 17 | /** 18 | * Creates a set of data to be sorted. 19 | * 20 | * @author Nathan Fiedler 21 | */ 22 | public enum DataGenerator { 23 | 24 | /** 25 | * Generates a set of pseudo words, comprised of at least one letter, 26 | * up to the length of the longest (real) English word, using only 27 | * the lower-case letters. 28 | */ 29 | PSEUDO_WORD { 30 | 31 | /** Longest (real) word in English: antidisestablishmentarianism */ 32 | private static final int LONGEST = 28; 33 | /** Letters in the English alphabet (lower case only) */ 34 | private static final int ALPHABET = 26; 35 | 36 | @Override 37 | public String[] generate(DataSize size) { 38 | Random r = new Random(); 39 | String[] list = new String[size.getValue()]; 40 | StringBuilder sb = new StringBuilder(); 41 | for (int ii = 0; ii < list.length; ii++) { 42 | int length = r.nextInt(LONGEST) + 1; 43 | for (int jj = 0; jj < length; jj++) { 44 | int d = r.nextInt(ALPHABET); 45 | sb.append((char) ('a' + d)); 46 | } 47 | list[ii] = sb.toString(); 48 | sb.setLength(0); 49 | } 50 | return list; 51 | } 52 | 53 | @Override 54 | public String getDisplayName() { 55 | return "Pseudo words FL28 C26"; 56 | } 57 | }, 58 | /** 59 | * Generates strings of a fixed length, comprised of randomly selected 60 | * characters from the printable ASCII set (from 32 to 126). 61 | */ 62 | RANDOM { 63 | 64 | /** Size of the randomly generated strings. */ 65 | private static final int LENGTH = 100; 66 | /** All printable characters in US-ASCII. */ 67 | private static final int ALPHABET = 95; 68 | 69 | @Override 70 | public String[] generate(DataSize size) { 71 | Random r = new Random(); 72 | String[] list = new String[size.getValue()]; 73 | StringBuilder sb = new StringBuilder(); 74 | for (int ii = 0; ii < list.length; ii++) { 75 | for (int jj = 0; jj < LENGTH; jj++) { 76 | int d = r.nextInt(ALPHABET); 77 | sb.append((char) (' ' + d)); 78 | } 79 | list[ii] = sb.toString(); 80 | sb.setLength(0); 81 | } 82 | return list; 83 | } 84 | 85 | @Override 86 | public String getDisplayName() { 87 | return "Random FL100 C95"; 88 | } 89 | }, 90 | /** 91 | * Generates a set of duplicate strings, comprised of an alphabet 92 | * of size one, where each string is 100 characters. One of three 93 | * pathological cases created to stress test the sort. 94 | */ 95 | REPEAT { 96 | 97 | @Override 98 | public String[] generate(DataSize size) { 99 | int count = size.getValue(); 100 | List list = Collections.nCopies(count, 101 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 102 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); 103 | return list.toArray(new String[count]); 104 | } 105 | 106 | @Override 107 | public String getDisplayName() { 108 | return "Repeated 100-A"; 109 | } 110 | }, 111 | /** 112 | * Generates a set of strings, comprised of an alphabet of size one, 113 | * where length increases from one to 100 characters in a cycle. 114 | * One of three pathological cases created to stress test the sort. 115 | */ 116 | REPEAT_CYCLE { 117 | 118 | @Override 119 | public String[] generate(DataSize size) { 120 | String[] strs = new String[Math.min(size.getValue() / 4, 100)]; 121 | String seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 122 | + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 123 | for (int i = 0; i < strs.length; i++) { 124 | strs[i] = seed.substring(0, i + 1); 125 | } 126 | String[] list = new String[size.getValue()]; 127 | int c = 0; 128 | for (int ii = 0; ii < list.length; ii++) { 129 | list[ii] = strs[c]; 130 | c++; 131 | if (c >= strs.length) { 132 | c = 0; 133 | } 134 | } 135 | return list; 136 | } 137 | 138 | @Override 139 | public String getDisplayName() { 140 | return "Repeated 100-A decreasing cycle"; 141 | } 142 | }, 143 | /** 144 | * Generates a set of strings, comprised of one to 100 characters, 145 | * from an alphabet consisting of nine letters. One of three 146 | * pathological cases to stress test the sort. 147 | */ 148 | SMALL_ALPHABET { 149 | 150 | /** Longest string to be created. */ 151 | private static final int LONGEST = 100; 152 | /** Small alphabet size. */ 153 | private static final int ALPHABET = 9; 154 | 155 | @Override 156 | public String[] generate(DataSize size) { 157 | Random r = new Random(); 158 | String[] list = new String[size.getValue()]; 159 | StringBuilder sb = new StringBuilder(); 160 | for (int ii = 0; ii < list.length; ii++) { 161 | int length = r.nextInt(LONGEST) + 1; 162 | for (int jj = 0; jj < length; jj++) { 163 | int d = r.nextInt(ALPHABET); 164 | sb.append((char) ('a' + d)); 165 | } 166 | list[ii] = sb.toString(); 167 | sb.setLength(0); 168 | } 169 | return list; 170 | } 171 | 172 | @Override 173 | public String getDisplayName() { 174 | return "Small Alphabet FL100 C9"; 175 | } 176 | }, 177 | /** 178 | * Generates strings of a fixed length, comprised of randomly selected 179 | * characters from the genome alphabet. 180 | */ 181 | GENOME { 182 | 183 | /** Size of the randomly generated strings. */ 184 | private static final int LENGTH = 9; 185 | /** Size of the genome alphabet (a, c, g, t). */ 186 | private static final int ALPHABET = 4; 187 | 188 | @Override 189 | public String[] generate(DataSize size) { 190 | Random r = new Random(); 191 | String[] list = new String[size.getValue()]; 192 | StringBuilder sb = new StringBuilder(); 193 | for (int ii = 0; ii < list.length; ii++) { 194 | for (int jj = 0; jj < LENGTH; jj++) { 195 | switch (r.nextInt(ALPHABET)) { 196 | case 0: 197 | sb.append('a'); 198 | break; 199 | case 1: 200 | sb.append('c'); 201 | break; 202 | case 2: 203 | sb.append('g'); 204 | break; 205 | case 3: 206 | sb.append('t'); 207 | break; 208 | } 209 | } 210 | list[ii] = sb.toString(); 211 | sb.setLength(0); 212 | } 213 | return list; 214 | } 215 | 216 | @Override 217 | public String getDisplayName() { 218 | return "Genome FL9 C4"; 219 | } 220 | }, 221 | /** 222 | * Generates a data set that would normally cause worst-case behavior 223 | * for a sorting algorithm such as quicksort, otherwise known as a 224 | * "median of 3 killer". 225 | */ 226 | MEDIAN_OF_3_KILLER { 227 | 228 | @Override 229 | public String[] generate(DataSize size) { 230 | if (((size.getValue() / 2) % 2) == 1) { 231 | throw new IllegalArgumentException( 232 | "cannot generate median-of-3 killer with given size"); 233 | } 234 | // Generate a random data set and then sort it so we can 235 | // pluck values from it to generate our killer data set. 236 | String[] data = PSEUDO_WORD.generate(size); 237 | DualPivotQuicksort.sort(data); 238 | final int k = data.length / 2; 239 | String[] list = new String[data.length]; 240 | for (int ii = 1; ii <= k; ii++) { 241 | if ((ii % 2) == 1) { 242 | list[ii - 1] = data[ii - 1]; 243 | list[ii] = data[k + ii - 1]; 244 | } 245 | list[k + ii - 1] = data[2 * ii - 1]; 246 | } 247 | return list; 248 | } 249 | 250 | @Override 251 | public String getDisplayName() { 252 | return "Median3Killer"; 253 | } 254 | }, 255 | /** 256 | * A "generator" that reads data from a named file, returning 257 | * a particular number of lines based on the requested size. 258 | * The file must have sufficient data or an error occurs. 259 | */ 260 | FILE { 261 | 262 | /** File from whence data is to be read. */ 263 | private File file; 264 | 265 | @Override 266 | public void setFile(File file) { 267 | // Yeah, this is basically a static field, but I don't care. 268 | this.file = file; 269 | } 270 | 271 | @Override 272 | public String[] generate(DataSize size) { 273 | int count = size.getValue(); 274 | String[] data = new String[count]; 275 | try { 276 | FileReader fr = new FileReader(file); 277 | BufferedReader br = new BufferedReader(fr); 278 | String line = br.readLine(); 279 | for (int ii = 0; count > 0; ii++, count--) { 280 | if (line == null) { 281 | break; 282 | } 283 | data[ii] = line; 284 | line = br.readLine(); 285 | } 286 | } catch (IOException ioe) { 287 | throw new RuntimeException(ioe); 288 | } 289 | if (count > 0) { 290 | throw new RuntimeException(String.format( 291 | "File '%s' has too few lines (%d more needed)", 292 | file.getName(), count)); 293 | } 294 | return data; 295 | } 296 | 297 | @Override 298 | public String getDisplayName() { 299 | return file.getName(); 300 | } 301 | }; 302 | 303 | /** 304 | * Generate data for testing the sort implementations. 305 | * 306 | * @param size size of the data to be generated. 307 | * @return array of test data. 308 | */ 309 | public abstract String[] generate(DataSize size); 310 | 311 | /** 312 | * Returns the display name for this generator. 313 | * 314 | * @return display name. 315 | */ 316 | public abstract String getDisplayName(); 317 | 318 | /** 319 | * For certain data generators, a file is needed to produce the data. 320 | * 321 | * @param file that which contains test data. 322 | */ 323 | public void setFile(File file) { 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/org/burstsort4j/benchmark/HugeBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2011 Nathan Fiedler. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | package org.burstsort4j.benchmark; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Runs performance tests over several kinds of data for each of the 16 | * sort implementations, collecting run times and displaying the results. 17 | * 18 | * @author Nathan Fiedler 19 | */ 20 | public class HugeBenchmark { 21 | 22 | /** Number of times each sort implementation is run for each data set. */ 23 | private static final int RUN_COUNT = 5; 24 | 25 | /** 26 | * Creates a new instance of Benchmark. 27 | */ 28 | private HugeBenchmark() { 29 | } 30 | 31 | /** 32 | * Command-line interface to benchmark driver. 33 | * 34 | * @param args command-line arguments. 35 | */ 36 | public static void main(String[] args) { 37 | DataGenerator[] generators = { 38 | DataGenerator.RANDOM, 39 | DataGenerator.REPEAT, 40 | DataGenerator.REPEAT_CYCLE, 41 | DataGenerator.PSEUDO_WORD, 42 | DataGenerator.GENOME, 43 | DataGenerator.SMALL_ALPHABET, 44 | DataGenerator.MEDIAN_OF_3_KILLER 45 | }; 46 | SortRunner[] runners = null; 47 | if (Runtime.getRuntime().availableProcessors() > 1) { 48 | runners = new SortRunner[]{ 49 | SortRunner.MERGE, 50 | SortRunner.QUICK, 51 | SortRunner.MULTIKEY, 52 | SortRunner.BURST, 53 | SortRunner.BURST_THREADPOOL, 54 | SortRunner.REDESIGNED_BURST, 55 | SortRunner.REDESIGNED_BURST_THREADPOOL, 56 | SortRunner.LAZY_FUNNEL, 57 | SortRunner.THREADED_LAZY_FUNNEL 58 | }; 59 | } else { 60 | runners = new SortRunner[]{ 61 | SortRunner.MERGE, 62 | SortRunner.QUICK, 63 | SortRunner.MULTIKEY, 64 | SortRunner.BURST, 65 | SortRunner.REDESIGNED_BURST, 66 | SortRunner.LAZY_FUNNEL 67 | }; 68 | } 69 | DataSize[] sizes = { 70 | DataSize.N_512000, 71 | DataSize.N_1024000, 72 | DataSize.N_3000000 73 | }; 74 | if (args.length > 0) { 75 | // Parse the command line arguments. 76 | int i = 0; 77 | while (i < args.length) { 78 | if (args[i].equals("--comparable")) { 79 | // Benchmark the Comparable-based sorters (i.e. those that 80 | // sort instances of Comparable, without any assumptions 81 | // about the input, such as String-based sorters). 82 | runners = new SortRunner[]{ 83 | SortRunner.MERGE, 84 | SortRunner.QUICK, 85 | SortRunner.LAZY_FUNNEL 86 | }; 87 | } else if (args[i].equals("--data")) { 88 | i++; 89 | if (i >= args.length) { 90 | usage("Missing --data argument"); 91 | } 92 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 93 | List list = new ArrayList(); 94 | for (DataGenerator generator : generators) { 95 | Matcher m = p.matcher(generator.getDisplayName()); 96 | if (m.find()) { 97 | list.add(generator); 98 | } 99 | } 100 | generators = list.toArray(new DataGenerator[list.size()]); 101 | } else if (args[i].equals("--sort")) { 102 | i++; 103 | if (i >= args.length) { 104 | usage("Missing --sort argument"); 105 | } 106 | Pattern p = Pattern.compile(args[i], Pattern.CASE_INSENSITIVE); 107 | List list = new ArrayList(); 108 | for (SortRunner runner : runners) { 109 | Matcher m = p.matcher(runner.getDisplayName()); 110 | if (m.find()) { 111 | list.add(runner); 112 | } 113 | } 114 | runners = list.toArray(new SortRunner[list.size()]); 115 | } else if (args[i].equals("--list")) { 116 | System.out.println("Data sets"); 117 | for (DataGenerator generator : generators) { 118 | System.out.format("\t%s\n", generator.getDisplayName()); 119 | } 120 | System.out.println("Sorting algorithms"); 121 | for (SortRunner runner : runners) { 122 | System.out.format("\t%s\n", runner.getDisplayName()); 123 | } 124 | System.exit(0); 125 | } else if (args[i].equals("--size")) { 126 | i++; 127 | if (i >= args.length) { 128 | usage("Missing --size argument"); 129 | } 130 | if (args[i].equals("small")) { 131 | sizes = new DataSize[]{DataSize.N_512000}; 132 | } else if (args[i].equals("medium")) { 133 | sizes = new DataSize[]{DataSize.N_1024000}; 134 | } else if (args[i].equals("large")) { 135 | sizes = new DataSize[]{DataSize.N_3000000}; 136 | } else { 137 | usage("Unrecognized --size argument"); 138 | } 139 | } else if (args[i].equals("--file")) { 140 | i++; 141 | if (i >= args.length) { 142 | usage("Missing --file argument"); 143 | } 144 | File file = new File(args[i]); 145 | if (!file.exists()) { 146 | usage("File not found: " + args[i]); 147 | } 148 | DataGenerator fgen = DataGenerator.FILE; 149 | fgen.setFile(file); 150 | generators = new DataGenerator[]{fgen}; 151 | } else if (args[i].equals("--help")) { 152 | usage(); 153 | } else { 154 | usage("Unrecognized option: " + args[i]); 155 | } 156 | i++; 157 | } 158 | } 159 | runsorts(generators, runners, sizes); 160 | } 161 | 162 | /** 163 | * Display an error message and the usage information. 164 | */ 165 | private static void usage(String msg) { 166 | System.out.println(msg); 167 | usage(); 168 | } 169 | 170 | /** 171 | * Display a usage message. 172 | */ 173 | private static void usage() { 174 | System.out.println("Usage: Benchmark [options]"); 175 | System.out.println("\t--comparable"); 176 | System.out.println("\t\tRun only the sorts that operate on Comparable."); 177 | System.out.println("\t\tNot compatible with the --sort option."); 178 | System.out.println("\t--data "); 179 | System.out.println("\t\tSelect the data set whose name matches the regular expression."); 180 | System.out.println("\t\tFor example, '--data random' would use only the random data set."); 181 | System.out.println("\t\tNot compatible with the --file option."); 182 | System.out.println("\t--file "); 183 | System.out.println("\t\tUse the contents of the named file for sorting."); 184 | System.out.println("\t\tNot compatible with the --data option."); 185 | System.out.println("\t--help"); 186 | System.out.println("\t\tDisplay this usage information."); 187 | System.out.println("\t--list"); 188 | System.out.println("\t\tDisplay a list of the supported data sets and sorting algorithms."); 189 | System.out.println("\t--size "); 190 | System.out.println("\t\tIf given 'small', uses 512,000 inputs from data set."); 191 | System.out.println("\t\tIf given 'medium', uses 1,024,000 inputs from data set."); 192 | System.out.println("\t\tIf given 'large', uses 3,000,000 inputs from data set."); 193 | System.out.println("\t--sort "); 194 | System.out.println("\t\tSelect the sort algorithms whose name matches the regular"); 195 | System.out.println("\t\texpression. For example, '--sort (comb|insert)' would run"); 196 | System.out.println("\t\tboth versions of the insertion and comb sort algorithms."); 197 | System.out.println("\t\tNot compatible with the --comparable option."); 198 | System.exit(0); 199 | } 200 | 201 | /** 202 | * Runs a set of sort routines over test data, as provided by the 203 | * given data generators. Performs a warmup run first to get all 204 | * of the classes compiled by the JVM, to avoid skewing the resuls. 205 | * 206 | * @param generators set of data generators to use. 207 | * @param runners set of sorters to compare. 208 | * @param sizes data sizes to be run. 209 | */ 210 | private static void runsorts(DataGenerator[] generators, 211 | SortRunner[] runners, DataSize[] sizes) { 212 | 213 | // Warm up the JVM so that the code (hopefully) gets compiled. 214 | System.out.println("Warming up the system, please wait..."); 215 | String[] input = new String[DataSize.N_512000.getValue()]; 216 | for (DataGenerator generator : generators) { 217 | String[] dataSet = generator.generate(DataSize.N_512000); 218 | for (SortRunner runner : runners) { 219 | System.arraycopy(dataSet, 0, input, 0, input.length); 220 | runner.sort(input); 221 | } 222 | } 223 | 224 | // For each type of data set, and each data set size, and 225 | // each sort implementation, run the sort several times and 226 | // calculate the average run time. 227 | for (DataGenerator generator : generators) { 228 | System.out.format("%s...\n", generator.getDisplayName()); 229 | for (DataSize size : sizes) { 230 | System.out.format("\t%s...\n", size.toString()); 231 | String[] dataSet = generator.generate(size); 232 | input = new String[size.getValue()]; 233 | for (SortRunner runner : runners) { 234 | System.out.format("\t\t%-20s:\t", runner.getDisplayName()); 235 | long[] times = new long[RUN_COUNT]; 236 | for (int run = 0; run < times.length; run++) { 237 | System.arraycopy(dataSet, 0, input, 0, input.length); 238 | long t1 = System.currentTimeMillis(); 239 | runner.sort(input); 240 | long t2 = System.currentTimeMillis(); 241 | times[run] = t2 - t1; 242 | } 243 | 244 | // Find the average of the run times. The run times 245 | // should never be more than a couple of minutes, 246 | // so these calculations will never overflow. 247 | long total = 0; 248 | long highest = Long.MIN_VALUE; 249 | long lowest = Long.MAX_VALUE; 250 | for (int run = 0; run < RUN_COUNT; run++) { 251 | total += times[run]; 252 | if (times[run] > highest) { 253 | highest = times[run]; 254 | } 255 | if (times[run] < lowest) { 256 | lowest = times[run]; 257 | } 258 | } 259 | long average = total / RUN_COUNT; 260 | System.out.format("%4d %4d %4d (low/avg/high) ms\n", lowest, average, highest); 261 | } 262 | } 263 | } 264 | } 265 | } 266 | --------------------------------------------------------------------------------