From 7b3992bef3cf41d0093c6458d2bc811817594064 Mon Sep 17 00:00:00 2001 From: Emir Pasic Date: Sat, 25 Jun 2016 05:35:13 +0200 Subject: [PATCH] - replace timsort with go's sort --- README.md | 13 +- utils/sort.go | 23 +- utils/sort_test.go | 24 + utils/timsort/LICENSE | 19 - utils/timsort/timsort.go | 1148 -------------------------------------- 5 files changed, 54 insertions(+), 1173 deletions(-) delete mode 100644 utils/timsort/LICENSE delete mode 100644 utils/timsort/timsort.go diff --git a/README.md b/README.md index 31248e1..e5297f5 100644 --- a/README.md +++ b/README.md @@ -995,8 +995,17 @@ Biggest contribution towards this library is to use it and give us feedback for For direct contributions, _pull request_ into master or ask to become a contributor. +Coding style: + +```shell +# Install tooling: +go build github.com/golang/lint/golint +go build github.com/fzipp/gocyclo + +# Fix errors and warnings: +go fmt ./... && gofmt -s -w . && go vet ./... && go get ./... && go test ./... && golint ./... && gocyclo -avg -over 15 . +``` + ### License This library is distributed under the BSD-style license found in the [LICENSE](https://github.com/emirpasic/gods/blob/master/LICENSE) file. - -TimSort copied from [https://github.com/psilva261/timsort](https://github.com/psilva261/timsort) with MIT [LICENSE](https://github.com/emirpasic/gods/blob/master/utils/timsort/LICENSE) file. diff --git a/utils/sort.go b/utils/sort.go index 04b5721..3cf8c1f 100644 --- a/utils/sort.go +++ b/utils/sort.go @@ -28,10 +28,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package utils -import "github.com/emirpasic/gods/utils/timsort" +import "sort" -// Sort sorts values (in-place) using timsort +// Sort sorts values (in-place) +// Uses Go's sort (hybrid of quicksort for large and then insertion sort for smaller slices) func Sort(values []interface{}, comparator Comparator) { - less := func(a, b interface{}) bool { return comparator(a, b) < 0 } - timsort.Sort(values, less) + sort.Sort(sortable{values, comparator}) +} + +type sortable struct { + values []interface{} + comparator Comparator +} + +func (s sortable) Len() int { + return len(s.values) +} +func (s sortable) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] +} +func (s sortable) Less(i, j int) bool { + return s.comparator(s.values[i], s.values[j]) < 0 } diff --git a/utils/sort_test.go b/utils/sort_test.go index d1e523b..f6d546f 100644 --- a/utils/sort_test.go +++ b/utils/sort_test.go @@ -27,6 +27,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package utils import ( + "math/rand" "testing" ) @@ -98,5 +99,28 @@ func TestSortStructs(t *testing.T) { t.Errorf("Not sorted!") } } +} + +func TestSortRandom(t *testing.T) { + ints := []interface{}{} + for i := 0; i < 10000; i++ { + ints = append(ints, rand.Int()) + } + Sort(ints, IntComparator) + for i := 1; i < len(ints); i++ { + if ints[i-1].(int) > ints[i].(int) { + t.Errorf("Not sorted!") + } + } +} +func BenchmarkGoSortRandom(b *testing.B) { + b.StopTimer() + ints := []interface{}{} + for i := 0; i < 100000; i++ { + ints = append(ints, rand.Int()) + } + b.StartTimer() + Sort(ints, IntComparator) + b.StopTimer() } diff --git a/utils/timsort/LICENSE b/utils/timsort/LICENSE deleted file mode 100644 index 208ba42..0000000 --- a/utils/timsort/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2010-2011 Mike Kroutikov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/utils/timsort/timsort.go b/utils/timsort/timsort.go deleted file mode 100644 index 6404901..0000000 --- a/utils/timsort/timsort.go +++ /dev/null @@ -1,1148 +0,0 @@ -// Obtained from: https://github.com/psilva261/timsort - -/* -Copyright (c) 2010-2011 Mike Kroutikov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE -*/ - -// Fast stable sort, uses external comparator. -// -// A stable, adaptive, iterative mergesort that requires far fewer than -// n lg(n) comparisons when running on partially sorted arrays, while -// offering performance comparable to a traditional mergesort when run -// on random arrays. Like all proper mergesorts, this sort is stable and -// runs O(n log n) time (worst case). In the worst case, this sort requires -// temporary storage space for n/2 object references; in the best case, -// it requires only a small constant amount of space. -// -// This implementation was derived from Java's TimSort object by Josh Bloch, -// which, in turn, was based on the original code by Tim Peters: -// -// http://svn.python.org/projects/python/trunk/Objects/listsort.txt -// -// Mike K. - -package timsort - -import ( - "errors" - "fmt" -) - -const ( - /** - * This is the minimum sized sequence that will be merged. Shorter - * sequences will be lengthened by calling binarySort. If the entire - * array is less than this length, no merges will be performed. - * - * This constant should be a power of two. It was 64 in Tim Peter's C - * implementation, but 32 was empirically determined to work better in - * this implementation. In the unlikely event that you set this constant - * to be a number that's not a power of two, you'll need to change the - * {@link #minRunLength} computation. - * - * If you decrease this constant, you must change the stackLen - * computation in the TimSort constructor, or you risk an - * ArrayOutOfBounds exception. See listsort.txt for a discussion - * of the minimum stack length required as a function of the length - * of the array being sorted and the minimum merge sequence length. - */ - minMerge = 32 - // mk: tried higher MIN_MERGE and got slower sorting (348->375) - // c_MIN_MERGE = 64 - - /** - * When we get into galloping mode, we stay there until both runs win less - * often than c_MIN_GALLOP consecutive times. - */ - minGallop = 7 - - /** - * Maximum initial size of tmp array, which is used for merging. The array - * can grow to accommodate demand. - * - * Unlike Tim's original C version, we do not allocate this much storage - * when sorting smaller arrays. This change was required for performance. - */ - initialTmpStorageLength = 256 -) - -// LessThan delegate type for sorting uses as a comparator -type LessThan func(a, b interface{}) bool - -type timSortHandler struct { - - /** - * The array being sorted. - */ - a []interface{} - - /** - * The comparator for this sort. - */ - lt LessThan - - /** - * This controls when we get *into* galloping mode. It is initialized - * to c_MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for - * random data, and lower for highly structured data. - */ - minGallop int - - /** - * Temp storage for merges. - */ - tmp []interface{} // Actual runtime type will be Object[], regardless of T - - /** - * A stack of pending runs yet to be merged. Run i starts at - * address base[i] and extends for len[i] elements. It's always - * true (so long as the indices are in bounds) that: - * - * runBase[i] + runLen[i] == runBase[i + 1] - * - * so we could cut the storage for this, but it's a minor amount, - * and keeping all the info explicit simplifies the code. - */ - stackSize int // Number of pending runs on stack - runBase []int - runLen []int -} - -/** - * Creates a TimSort instance to maintain the state of an ongoing sort. - * - * @param a the array to be sorted - * @param c the comparator to determine the order of the sort - */ -func newTimSort(a []interface{}, lt LessThan) (tsh *timSortHandler) { - tsh = new(timSortHandler) - - tsh.a = a - tsh.lt = lt - tsh.minGallop = minGallop - tsh.stackSize = 0 - - // Allocate temp storage (which may be increased later if necessary) - len := len(a) - - tmpSize := initialTmpStorageLength - if len < 2*tmpSize { - tmpSize = len / 2 - } - - tsh.tmp = make([]interface{}, tmpSize) - - /* - * Allocate runs-to-be-merged stack (which cannot be expanded). The - * stack length requirements are described in listsort.txt. The C - * version always uses the same stack length (85), but this was - * measured to be too expensive when sorting "mid-sized" arrays (e.g., - * 100 elements) in Java. Therefore, we use smaller (but sufficiently - * large) stack lengths for smaller arrays. The "magic numbers" in the - * computation below must be changed if c_MIN_MERGE is decreased. See - * the c_MIN_MERGE declaration above for more information. - */ - // mk: confirmed that for small sorts this optimization gives measurable (albeit small) - // performance enhancement - stackLen := 40 - if len < 120 { - stackLen = 5 - } else if len < 1542 { - stackLen = 10 - } else if len < 119151 { - stackLen = 19 - } - - tsh.runBase = make([]int, stackLen) - tsh.runLen = make([]int, stackLen) - - return tsh -} - -// Sort sorts an array using the provided comparator -func Sort(a []interface{}, lt LessThan) (err error) { - lo := 0 - hi := len(a) - nRemaining := hi - - if nRemaining < 2 { - return // Arrays of size 0 and 1 are always sorted - } - - // If array is small, do a "mini-TimSort" with no merges - if nRemaining < minMerge { - initRunLen, err := countRunAndMakeAscending(a, lo, hi, lt) - if err != nil { - return err - } - - return binarySort(a, lo, hi, lo+initRunLen, lt) - } - - /** - * March over the array once, left to right, finding natural runs, - * extending short natural runs to minRun elements, and merging runs - * to maintain stack invariant. - */ - - ts := newTimSort(a, lt) - minRun, err := minRunLength(nRemaining) - if err != nil { - return - } - for { - // Identify next run - runLen, err := countRunAndMakeAscending(a, lo, hi, lt) - if err != nil { - return err - } - - // If run is short, extend to min(minRun, nRemaining) - if runLen < minRun { - force := minRun - if nRemaining <= minRun { - force = nRemaining - } - if err = binarySort(a, lo, lo+force, lo+runLen, lt); err != nil { - return err - } - runLen = force - } - - // Push run onto pending-run stack, and maybe merge - ts.pushRun(lo, runLen) - if err = ts.mergeCollapse(); err != nil { - return err - } - - // Advance to find next run - lo += runLen - nRemaining -= runLen - if nRemaining == 0 { - break - } - } - - // Merge all remaining runs to complete sort - if lo != hi { - return errors.New("lo==hi") - } - - if err = ts.mergeForceCollapse(); err != nil { - return - } - if ts.stackSize != 1 { - return errors.New("ts.stackSize != 1") - } - return -} - -/** - * Sorts the specified portion of the specified array using a binary - * insertion sort. This is the best method for sorting small numbers - * of elements. It requires O(n log n) compares, but O(n^2) data - * movement (worst case). - * - * If the initial part of the specified range is already sorted, - * this method can take advantage of it: the method assumes that the - * elements from index {@code lo}, inclusive, to {@code start}, - * exclusive are already sorted. - * - * @param a the array in which a range is to be sorted - * @param lo the index of the first element in the range to be sorted - * @param hi the index after the last element in the range to be sorted - * @param start the index of the first element in the range that is - * not already known to be sorted (@code lo <= start <= hi} - * @param c comparator to used for the sort - */ -func binarySort(a []interface{}, lo, hi, start int, lt LessThan) (err error) { - if lo > start || start > hi { - return errors.New("lo <= start && start <= hi") - } - - if start == lo { - start++ - } - - for ; start < hi; start++ { - pivot := a[start] - - // Set left (and right) to the index where a[start] (pivot) belongs - left := lo - right := start - - if left > right { - return errors.New("left <= right") - } - - /* - * Invariants: - * pivot >= all in [lo, left). - * pivot < all in [right, start). - */ - for left < right { - mid := (left + right) / 2 - if lt(pivot, a[mid]) { - right = mid - } else { - left = mid + 1 - } - } - - if left != right { - return errors.New("left == right") - } - - /* - * The invariants still hold: pivot >= all in [lo, left) and - * pivot < all in [left, start), so pivot belongs at left. Note - * that if there are elements equal to pivot, left points to the - * first slot after them -- that's why this sort is stable. - * Slide elements over to make room to make room for pivot. - */ - n := start - left // The number of elements to move - // just an optimization for copy in default case - if n <= 2 { - if n == 2 { - a[left+2] = a[left+1] - } - if n > 0 { - a[left+1] = a[left] - } - } else { - copy(a[left+1:], a[left:left+n]) - } - a[left] = pivot - } - return -} - -/** - * Returns the length of the run beginning at the specified position in - * the specified array and reverses the run if it is descending (ensuring - * that the run will always be ascending when the method returns). - * - * A run is the longest ascending sequence with: - * - * a[lo] <= a[lo + 1] <= a[lo + 2] <= ... - * - * or the longest descending sequence with: - * - * a[lo] > a[lo + 1] > a[lo + 2] > ... - * - * For its intended use in a stable mergesort, the strictness of the - * definition of "descending" is needed so that the call can safely - * reverse a descending sequence without violating stability. - * - * @param a the array in which a run is to be counted and possibly reversed - * @param lo index of the first element in the run - * @param hi index after the last element that may be contained in the run. - It is required that @code{lo < hi}. - * @param c the comparator to used for the sort - * @return the length of the run beginning at the specified position in - * the specified array -*/ -func countRunAndMakeAscending(a []interface{}, lo, hi int, lt LessThan) (int, error) { - - if lo >= hi { - return 0, errors.New("lo < hi") - } - - runHi := lo + 1 - if runHi == hi { - return 1, nil - } - - // Find end of run, and reverse range if descending - if lt(a[runHi], a[lo]) { // Descending - runHi++ - - for runHi < hi && lt(a[runHi], a[runHi-1]) { - runHi++ - } - reverseRange(a, lo, runHi) - } else { // Ascending - for runHi < hi && !lt(a[runHi], a[runHi-1]) { - runHi++ - } - } - - return runHi - lo, nil -} - -/** - * Reverse the specified range of the specified array. - * - * @param a the array in which a range is to be reversed - * @param lo the index of the first element in the range to be reversed - * @param hi the index after the last element in the range to be reversed - */ -func reverseRange(a []interface{}, lo, hi int) { - hi-- - for lo < hi { - a[lo], a[hi] = a[hi], a[lo] - lo++ - hi-- - } -} - -/** - * Returns the minimum acceptable run length for an array of the specified - * length. Natural runs shorter than this will be extended with - * {@link #binarySort}. - * - * Roughly speaking, the computation is: - * - * If n < c_MIN_MERGE, return n (it's too small to bother with fancy stuff). - * Else if n is an exact power of 2, return c_MIN_MERGE/2. - * Else return an int k, c_MIN_MERGE/2 <= k <= c_MIN_MERGE, such that n/k - * is close to, but strictly less than, an exact power of 2. - * - * For the rationale, see listsort.txt. - * - * @param n the length of the array to be sorted - * @return the length of the minimum run to be merged - */ -func minRunLength(n int) (int, error) { - if n < 0 { - return 0, errors.New("n >= 0") - } - r := 0 // Becomes 1 if any 1 bits are shifted off - for n >= minMerge { - r |= (n & 1) - n >>= 1 - } - return n + r, nil -} - -/** - * Pushes the specified run onto the pending-run stack. - * - * @param runBase index of the first element in the run - * @param runLen the number of elements in the run - */ -func (tsh *timSortHandler) pushRun(runBase, runLen int) { - tsh.runBase[tsh.stackSize] = runBase - tsh.runLen[tsh.stackSize] = runLen - tsh.stackSize++ -} - -/** - * Examines the stack of runs waiting to be merged and merges adjacent runs - * until the stack invariants are reestablished: - * - * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1] - * 2. runLen[i - 2] > runLen[i - 1] - * - * This method is called each time a new run is pushed onto the stack, - * so the invariants are guaranteed to hold for i < stackSize upon - * entry to the method. - */ -func (tsh *timSortHandler) mergeCollapse() (err error) { - for tsh.stackSize > 1 { - n := tsh.stackSize - 2 - if n > 0 && tsh.runLen[n-1] <= tsh.runLen[n]+tsh.runLen[n+1] { - if tsh.runLen[n-1] < tsh.runLen[n+1] { - n-- - } - if err = tsh.mergeAt(n); err != nil { - return - } - } else if tsh.runLen[n] <= tsh.runLen[n+1] { - if err = tsh.mergeAt(n); err != nil { - return - } - } else { - break // Invariant is established - } - } - return -} - -/** - * Merges all runs on the stack until only one remains. This method is - * called once, to complete the sort. - */ -func (tsh *timSortHandler) mergeForceCollapse() (err error) { - for tsh.stackSize > 1 { - n := tsh.stackSize - 2 - if n > 0 && tsh.runLen[n-1] < tsh.runLen[n+1] { - n-- - } - if err = tsh.mergeAt(n); err != nil { - return - } - } - return -} - -/** - * Merges the two runs at stack indices i and i+1. Run i must be - * the penultimate or antepenultimate run on the stack. In other words, - * i must be equal to stackSize-2 or stackSize-3. - * - * @param i stack index of the first of the two runs to merge - */ -func (tsh *timSortHandler) mergeAt(i int) (err error) { - if tsh.stackSize < 2 { - return errors.New("stackSize >= 2") - } - - if i < 0 { - return errors.New(" i >= 0") - } - - if i != tsh.stackSize-2 && i != tsh.stackSize-3 { - return errors.New("if i == stackSize - 2 || i == stackSize - 3") - } - - base1 := tsh.runBase[i] - len1 := tsh.runLen[i] - base2 := tsh.runBase[i+1] - len2 := tsh.runLen[i+1] - - if len1 <= 0 || len2 <= 0 { - return errors.New("len1 > 0 && len2 > 0") - } - - if base1+len1 != base2 { - return errors.New("base1 + len1 == base2") - } - - /* - * Record the length of the combined runs; if i is the 3rd-last - * run now, also slide over the last run (which isn't involved - * in this merge). The current run (i+1) goes away in any case. - */ - tsh.runLen[i] = len1 + len2 - if i == tsh.stackSize-3 { - tsh.runBase[i+1] = tsh.runBase[i+2] - tsh.runLen[i+1] = tsh.runLen[i+2] - } - tsh.stackSize-- - - /* - * Find where the first element of run2 goes in run1. Prior elements - * in run1 can be ignored (because they're already in place). - */ - k, err := gallopRight(tsh.a[base2], tsh.a, base1, len1, 0, tsh.lt) - if err != nil { - return err - } - if k < 0 { - return errors.New(" k >= 0;") - } - base1 += k - len1 -= k - if len1 == 0 { - return - } - - /* - * Find where the last element of run1 goes in run2. Subsequent elements - * in run2 can be ignored (because they're already in place). - */ - len2, err = gallopLeft(tsh.a[base1+len1-1], tsh.a, base2, len2, len2-1, tsh.lt) - if err != nil { - return - } - if len2 < 0 { - return errors.New(" len2 >= 0;") - } - if len2 == 0 { - return - } - - // Merge remaining runs, using tmp array with min(len1, len2) elements - if len1 <= len2 { - err = tsh.mergeLo(base1, len1, base2, len2) - if err != nil { - return errors.New(fmt.Errorf("mergeLo: %v", err).Error()) - } - } else { - err = tsh.mergeHi(base1, len1, base2, len2) - if err != nil { - return errors.New(fmt.Errorf("mergeHi: %v", err).Error()) - } - } - return -} - -/** - * Locates the position at which to insert the specified key into the - * specified sorted range; if the range contains an element equal to key, - * returns the index of the leftmost equal element. - * - * @param key the key whose insertion point to search for - * @param a the array in which to search - * @param base the index of the first element in the range - * @param len the length of the range; must be > 0 - * @param hint the index at which to begin the search, 0 <= hint < n. - * The closer hint is to the result, the faster this method will run. - * @param c the comparator used to order the range, and to search - * @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k], - * pretending that a[b - 1] is minus infinity and a[b + n] is infinity. - * In other words, key belongs at index b + k; or in other words, - * the first k elements of a should precede key, and the last n - k - * should follow it. - */ -func gallopLeft(key interface{}, a []interface{}, base, len, hint int, c LessThan) (int, error) { - if len <= 0 || hint < 0 || hint >= len { - return 0, errors.New(" len > 0 && hint >= 0 && hint < len;") - } - lastOfs := 0 - ofs := 1 - - if c(a[base+hint], key) { - // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs] - maxOfs := len - hint - for ofs < maxOfs && c(a[base+hint+ofs], key) { - lastOfs = ofs - ofs = (ofs << 1) + 1 - if ofs <= 0 { // int overflow - ofs = maxOfs - } - } - if ofs > maxOfs { - ofs = maxOfs - } - - // Make offsets relative to base - lastOfs += hint - ofs += hint - } else { // key <= a[base + hint] - // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs] - maxOfs := hint + 1 - for ofs < maxOfs && !c(a[base+hint-ofs], key) { - lastOfs = ofs - ofs = (ofs << 1) + 1 - if ofs <= 0 { // int overflow - ofs = maxOfs - } - } - if ofs > maxOfs { - ofs = maxOfs - } - - // Make offsets relative to base - tmp := lastOfs - lastOfs = hint - ofs - ofs = hint - tmp - } - - if -1 > lastOfs || lastOfs >= ofs || ofs > len { - return 0, errors.New(" -1 <= lastOfs && lastOfs < ofs && ofs <= len;") - } - - /* - * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere - * to the right of lastOfs but no farther right than ofs. Do a binary - * search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs]. - */ - lastOfs++ - for lastOfs < ofs { - m := lastOfs + (ofs-lastOfs)/2 - - if c(a[base+m], key) { - lastOfs = m + 1 // a[base + m] < key - } else { - ofs = m // key <= a[base + m] - } - } - - if lastOfs != ofs { - return 0, errors.New(" lastOfs == ofs") // so a[base + ofs - 1] < key <= a[base + ofs] - } - return ofs, nil -} - -/** - * Like gallopLeft, except that if the range contains an element equal to - * key, gallopRight returns the index after the rightmost equal element. - * - * @param key the key whose insertion point to search for - * @param a the array in which to search - * @param base the index of the first element in the range - * @param len the length of the range; must be > 0 - * @param hint the index at which to begin the search, 0 <= hint < n. - * The closer hint is to the result, the faster this method will run. - * @param c the comparator used to order the range, and to search - * @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k] - */ -func gallopRight(key interface{}, a []interface{}, base, len, hint int, c LessThan) (int, error) { - if len <= 0 || hint < 0 || hint >= len { - return 0, errors.New(" len > 0 && hint >= 0 && hint < len;") - } - - ofs := 1 - lastOfs := 0 - if c(key, a[base+hint]) { - // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs] - maxOfs := hint + 1 - for ofs < maxOfs && c(key, a[base+hint-ofs]) { - lastOfs = ofs - ofs = (ofs << 1) + 1 - if ofs <= 0 { // int overflow - ofs = maxOfs - } - } - if ofs > maxOfs { - ofs = maxOfs - } - - // Make offsets relative to b - tmp := lastOfs - lastOfs = hint - ofs - ofs = hint - tmp - } else { // a[b + hint] <= key - // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs] - maxOfs := len - hint - for ofs < maxOfs && !c(key, a[base+hint+ofs]) { - lastOfs = ofs - ofs = (ofs << 1) + 1 - if ofs <= 0 { // int overflow - ofs = maxOfs - } - } - if ofs > maxOfs { - ofs = maxOfs - } - - // Make offsets relative to b - lastOfs += hint - ofs += hint - } - if -1 > lastOfs || lastOfs >= ofs || ofs > len { - return 0, errors.New("-1 <= lastOfs && lastOfs < ofs && ofs <= len") - } - - /* - * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to - * the right of lastOfs but no farther right than ofs. Do a binary - * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs]. - */ - lastOfs++ - for lastOfs < ofs { - m := lastOfs + (ofs-lastOfs)/2 - - if c(key, a[base+m]) { - ofs = m // key < a[b + m] - } else { - lastOfs = m + 1 // a[b + m] <= key - } - } - if lastOfs != ofs { - return 0, errors.New(" lastOfs == ofs") // so a[b + ofs - 1] <= key < a[b + ofs] - } - return ofs, nil -} - -/** - * Merges two adjacent runs in place, in a stable fashion. The first - * element of the first run must be greater than the first element of the - * second run (a[base1] > a[base2]), and the last element of the first run - * (a[base1 + len1-1]) must be greater than all elements of the second run. - * - * For performance, this method should be called only when len1 <= len2; - * its twin, mergeHi should be called if len1 >= len2. (Either method - * may be called if len1 == len2.) - * - * @param base1 index of first element in first run to be merged - * @param len1 length of first run to be merged (must be > 0) - * @param base2 index of first element in second run to be merged - * (must be aBase + aLen) - * @param len2 length of second run to be merged (must be > 0) - */ -func (tsh *timSortHandler) mergeLo(base1, len1, base2, len2 int) (err error) { - if len1 <= 0 || len2 <= 0 || base1+len1 != base2 { - return errors.New(" len1 > 0 && len2 > 0 && base1 + len1 == base2") - } - - // Copy first run into temp array - a := tsh.a // For performance - tmp := tsh.ensureCapacity(len1) - - copy(tmp, a[base1:base1+len1]) - - cursor1 := 0 // Indexes into tmp array - cursor2 := base2 // Indexes int a - dest := base1 // Indexes int a - - // Move first element of second run and deal with degenerate cases - a[dest] = a[cursor2] - dest++ - cursor2++ - len2-- - if len2 == 0 { - copy(a[dest:dest+len1], tmp) - return - } - if len1 == 1 { - copy(a[dest:dest+len2], a[cursor2:cursor2+len2]) - a[dest+len2] = tmp[cursor1] // Last elt of run 1 to end of merge - return - } - - lt := tsh.lt // Use local variable for performance - minGallop := tsh.minGallop // " " " " " - -outer: - for { - count1 := 0 // Number of times in a row that first run won - count2 := 0 // Number of times in a row that second run won - - /* - * Do the straightforward thing until (if ever) one run starts - * winning consistently. - */ - for { - if len1 <= 1 || len2 <= 0 { - return errors.New(" len1 > 1 && len2 > 0") - } - - if lt(a[cursor2], tmp[cursor1]) { - a[dest] = a[cursor2] - dest++ - cursor2++ - count2++ - count1 = 0 - len2-- - if len2 == 0 { - break outer - } - } else { - a[dest] = tmp[cursor1] - dest++ - cursor1++ - count1++ - count2 = 0 - len1-- - if len1 == 1 { - break outer - } - } - if (count1 | count2) >= minGallop { - break - } - } - - /* - * One run is winning so consistently that galloping may be a - * huge win. So try that, and continue galloping until (if ever) - * neither run appears to be winning consistently anymore. - */ - for { - if len1 <= 1 || len2 <= 0 { - return errors.New("len1 > 1 && len2 > 0") - } - count1, err = gallopRight(a[cursor2], tmp, cursor1, len1, 0, lt) - if err != nil { - return - } - if count1 != 0 { - copy(a[dest:dest+count1], tmp[cursor1:cursor1+count1]) - dest += count1 - cursor1 += count1 - len1 -= count1 - if len1 <= 1 { // len1 == 1 || len1 == 0 - break outer - } - } - a[dest] = a[cursor2] - dest++ - cursor2++ - len2-- - if len2 == 0 { - break outer - } - - count2, err = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, lt) - if err != nil { - return - } - if count2 != 0 { - copy(a[dest:dest+count2], a[cursor2:cursor2+count2]) - dest += count2 - cursor2 += count2 - len2 -= count2 - if len2 == 0 { - break outer - } - } - a[dest] = tmp[cursor1] - dest++ - cursor1++ - len1-- - if len1 == 1 { - break outer - } - minGallop-- - if count1 < minGallop && count2 < minGallop { - break - } - } - if minGallop < 0 { - minGallop = 0 - } - minGallop += 2 // Penalize for leaving gallop mode - } // End of "outer" loop - - if minGallop < 1 { - minGallop = 1 - } - tsh.minGallop = minGallop // Write back to field - - if len1 == 1 { - - if len2 <= 0 { - return errors.New(" len2 > 0;") - } - copy(a[dest:dest+len2], a[cursor2:cursor2+len2]) - a[dest+len2] = tmp[cursor1] // Last elt of run 1 to end of merge - } else if len1 == 0 { - return errors.New("Comparison method violates its general contract!") - } else { - if len2 != 0 { - return errors.New("len2 == 0;") - } - if len1 <= 1 { - return errors.New(" len1 > 1;") - } - - copy(a[dest:dest+len1], tmp[cursor1:cursor1+len1]) - } - return -} - -/** - * Like mergeLo, except that this method should be called only if - * len1 >= len2; mergeLo should be called if len1 <= len2. (Either method - * may be called if len1 == len2.) - * - * @param base1 index of first element in first run to be merged - * @param len1 length of first run to be merged (must be > 0) - * @param base2 index of first element in second run to be merged - * (must be aBase + aLen) - * @param len2 length of second run to be merged (must be > 0) - */ -func (tsh *timSortHandler) mergeHi(base1, len1, base2, len2 int) (err error) { - if len1 <= 0 || len2 <= 0 || base1+len1 != base2 { - return errors.New("len1 > 0 && len2 > 0 && base1 + len1 == base2;") - } - - // Copy second run into temp array - a := tsh.a // For performance - tmp := tsh.ensureCapacity(len2) - - copy(tmp, a[base2:base2+len2]) - - cursor1 := base1 + len1 - 1 // Indexes into a - cursor2 := len2 - 1 // Indexes into tmp array - dest := base2 + len2 - 1 // Indexes into a - - // Move last element of first run and deal with degenerate cases - a[dest] = a[cursor1] - dest-- - cursor1-- - len1-- - if len1 == 0 { - dest -= len2 - 1 - copy(a[dest:dest+len2], tmp) - return - } - if len2 == 1 { - dest -= len1 - 1 - cursor1 -= len1 - 1 - copy(a[dest:dest+len1], a[cursor1:cursor1+len1]) - a[dest-1] = tmp[cursor2] - return - } - - lt := tsh.lt // Use local variable for performance - minGallop := tsh.minGallop // " " " " " - -outer: - for { - count1 := 0 // Number of times in a row that first run won - count2 := 0 // Number of times in a row that second run won - - /* - * Do the straightforward thing until (if ever) one run - * appears to win consistently. - */ - for { - if len1 <= 0 || len2 <= 1 { - return errors.New(" len1 > 0 && len2 > 1;") - } - if lt(tmp[cursor2], a[cursor1]) { - a[dest] = a[cursor1] - dest-- - cursor1-- - count1++ - count2 = 0 - len1-- - if len1 == 0 { - break outer - } - } else { - a[dest] = tmp[cursor2] - dest-- - cursor2-- - count2++ - count1 = 0 - len2-- - if len2 == 1 { - break outer - } - } - if (count1 | count2) >= minGallop { - break - } - } - - /* - * One run is winning so consistently that galloping may be a - * huge win. So try that, and continue galloping until (if ever) - * neither run appears to be winning consistently anymore. - */ - for { - if len1 <= 0 || len2 <= 1 { - return errors.New(" len1 > 0 && len2 > 1;") - } - if gr, err := gallopRight(tmp[cursor2], a, base1, len1, len1-1, lt); err == nil { - count1 = len1 - gr - } else { - return err - } - if count1 != 0 { - dest -= count1 - cursor1 -= count1 - len1 -= count1 - copy(a[dest+1:dest+1+count1], a[cursor1+1:cursor1+1+count1]) - if len1 == 0 { - break outer - } - } - a[dest] = tmp[cursor2] - dest-- - cursor2-- - len2-- - if len2 == 1 { - break outer - } - - if gl, err := gallopLeft(a[cursor1], tmp, 0, len2, len2-1, lt); err == nil { - count2 = len2 - gl - } else { - return err - } - if count2 != 0 { - dest -= count2 - cursor2 -= count2 - len2 -= count2 - copy(a[dest+1:dest+1+count2], tmp[cursor2+1:cursor2+1+count2]) - if len2 <= 1 { // len2 == 1 || len2 == 0 - break outer - } - } - a[dest] = a[cursor1] - dest-- - cursor1-- - len1-- - if len1 == 0 { - break outer - } - minGallop-- - - if count1 < minGallop && count2 < minGallop { - break - } - } - if minGallop < 0 { - minGallop = 0 - } - minGallop += 2 // Penalize for leaving gallop mode - } // End of "outer" loop - - if minGallop < 1 { - minGallop = 1 - } - - tsh.minGallop = minGallop // Write back to field - - if len2 == 1 { - if len1 <= 0 { - return errors.New(" len1 > 0;") - } - dest -= len1 - cursor1 -= len1 - - copy(a[dest+1:dest+1+len1], a[cursor1+1:cursor1+1+len1]) - a[dest] = tmp[cursor2] // Move first elt of run2 to front of merge - } else if len2 == 0 { - return errors.New("Comparison method violates its general contract!") - } else { - if len1 != 0 { - return errors.New("len1 == 0;") - } - - if len2 <= 0 { - return errors.New(" len2 > 0;") - } - - copy(a[dest-(len2-1):dest+1], tmp) - } - return -} - -/** - * Ensures that the external array tmp has at least the specified - * number of elements, increasing its size if necessary. The size - * increases exponentially to ensure amortized linear time complexity. - * - * @param minCapacity the minimum required capacity of the tmp array - * @return tmp, whether or not it grew - */ -func (tsh *timSortHandler) ensureCapacity(minCapacity int) []interface{} { - if len(tsh.tmp) < minCapacity { - // Compute smallest power of 2 > minCapacity - newSize := minCapacity - newSize |= newSize >> 1 - newSize |= newSize >> 2 - newSize |= newSize >> 4 - newSize |= newSize >> 8 - newSize |= newSize >> 16 - newSize++ - - if newSize < 0 { // Not bloody likely! - newSize = minCapacity - } else { - ns := len(tsh.a) / 2 - if ns < newSize { - newSize = ns - } - } - - tsh.tmp = make([]interface{}, newSize) - } - - return tsh.tmp -}