github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/base/bslice/zsortordered.go (about)

     1  // Code generated by gen_sort_variants.go; DO NOT EDIT.
     2  
     3  // Copyright 2022 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package bslice
     8  
     9  import (
    10  	"github.com/songzhibin97/go-baseutils/base/btype"
    11  )
    12  
    13  // insertionSortOrdered sorts data[a:b] using insertion sort.
    14  func insertionSortOrdered[E btype.Ordered](data []E, a, b int) {
    15  	for i := a + 1; i < b; i++ {
    16  		for j := i; j > a && (data[j] < data[j-1]); j-- {
    17  			data[j], data[j-1] = data[j-1], data[j]
    18  		}
    19  	}
    20  }
    21  
    22  // siftDownOrdered implements the heap property on data[lo:hi].
    23  // first is an offset into the array where the root of the heap lies.
    24  func siftDownOrdered[E btype.Ordered](data []E, lo, hi, first int) {
    25  	root := lo
    26  	for {
    27  		child := 2*root + 1
    28  		if child >= hi {
    29  			break
    30  		}
    31  		if child+1 < hi && (data[first+child] < data[first+child+1]) {
    32  			child++
    33  		}
    34  		if !(data[first+root] < data[first+child]) {
    35  			return
    36  		}
    37  		data[first+root], data[first+child] = data[first+child], data[first+root]
    38  		root = child
    39  	}
    40  }
    41  
    42  func heapSortOrdered[E btype.Ordered](data []E, a, b int) {
    43  	first := a
    44  	lo := 0
    45  	hi := b - a
    46  
    47  	// Build heap with greatest element at top.
    48  	for i := (hi - 1) / 2; i >= 0; i-- {
    49  		siftDownOrdered(data, i, hi, first)
    50  	}
    51  
    52  	// Pop elements, largest first, into end of data.
    53  	for i := hi - 1; i >= 0; i-- {
    54  		data[first], data[first+i] = data[first+i], data[first]
    55  		siftDownOrdered(data, lo, i, first)
    56  	}
    57  }
    58  
    59  // pdqsortOrdered sorts data[a:b].
    60  // The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
    61  // pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
    62  // C++ implementation: https://github.com/orlp/pdqsort
    63  // Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
    64  // limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
    65  func pdqsortOrdered[E btype.Ordered](data []E, a, b, limit int) {
    66  	const maxInsertion = 12
    67  
    68  	var (
    69  		wasBalanced    = true // whether the last partitioning was reasonably balanced
    70  		wasPartitioned = true // whether the slice was already partitioned
    71  	)
    72  
    73  	for {
    74  		length := b - a
    75  
    76  		if length <= maxInsertion {
    77  			insertionSortOrdered(data, a, b)
    78  			return
    79  		}
    80  
    81  		// Fall back to heapsort if too many bad choices were made.
    82  		if limit == 0 {
    83  			heapSortOrdered(data, a, b)
    84  			return
    85  		}
    86  
    87  		// If the last partitioning was imbalanced, we need to breaking patterns.
    88  		if !wasBalanced {
    89  			breakPatternsOrdered(data, a, b)
    90  			limit--
    91  		}
    92  
    93  		pivot, hint := choosePivotOrdered(data, a, b)
    94  		if hint == decreasingHint {
    95  			reverseRangeOrdered(data, a, b)
    96  			// The chosen pivot was pivot-a elements after the start of the array.
    97  			// After reversing it is pivot-a elements before the end of the array.
    98  			// The idea came from Rust's implementation.
    99  			pivot = (b - 1) - (pivot - a)
   100  			hint = increasingHint
   101  		}
   102  
   103  		// The slice is likely already sorted.
   104  		if wasBalanced && wasPartitioned && hint == increasingHint {
   105  			if partialInsertionSortOrdered(data, a, b) {
   106  				return
   107  			}
   108  		}
   109  
   110  		// Probably the slice contains many duplicate elements, partition the slice into
   111  		// elements equal to and elements greater than the pivot.
   112  		if a > 0 && !(data[a-1] < data[pivot]) {
   113  			mid := partitionEqualOrdered(data, a, b, pivot)
   114  			a = mid
   115  			continue
   116  		}
   117  
   118  		mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot)
   119  		wasPartitioned = alreadyPartitioned
   120  
   121  		leftLen, rightLen := mid-a, b-mid
   122  		balanceThreshold := length / 8
   123  		if leftLen < rightLen {
   124  			wasBalanced = leftLen >= balanceThreshold
   125  			pdqsortOrdered(data, a, mid, limit)
   126  			a = mid + 1
   127  		} else {
   128  			wasBalanced = rightLen >= balanceThreshold
   129  			pdqsortOrdered(data, mid+1, b, limit)
   130  			b = mid
   131  		}
   132  	}
   133  }
   134  
   135  // partitionOrdered does one quicksort partition.
   136  // Let p = data[pivot]
   137  // Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
   138  // On return, data[newpivot] = p
   139  func partitionOrdered[E btype.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
   140  	data[a], data[pivot] = data[pivot], data[a]
   141  	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
   142  
   143  	for i <= j && (data[i] < data[a]) {
   144  		i++
   145  	}
   146  	for i <= j && !(data[j] < data[a]) {
   147  		j--
   148  	}
   149  	if i > j {
   150  		data[j], data[a] = data[a], data[j]
   151  		return j, true
   152  	}
   153  	data[i], data[j] = data[j], data[i]
   154  	i++
   155  	j--
   156  
   157  	for {
   158  		for i <= j && (data[i] < data[a]) {
   159  			i++
   160  		}
   161  		for i <= j && !(data[j] < data[a]) {
   162  			j--
   163  		}
   164  		if i > j {
   165  			break
   166  		}
   167  		data[i], data[j] = data[j], data[i]
   168  		i++
   169  		j--
   170  	}
   171  	data[j], data[a] = data[a], data[j]
   172  	return j, false
   173  }
   174  
   175  // partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
   176  // It assumed that data[a:b] does not contain elements smaller than the data[pivot].
   177  func partitionEqualOrdered[E btype.Ordered](data []E, a, b, pivot int) (newpivot int) {
   178  	data[a], data[pivot] = data[pivot], data[a]
   179  	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
   180  
   181  	for {
   182  		for i <= j && !(data[a] < data[i]) {
   183  			i++
   184  		}
   185  		for i <= j && (data[a] < data[j]) {
   186  			j--
   187  		}
   188  		if i > j {
   189  			break
   190  		}
   191  		data[i], data[j] = data[j], data[i]
   192  		i++
   193  		j--
   194  	}
   195  	return i
   196  }
   197  
   198  // partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end.
   199  func partialInsertionSortOrdered[E btype.Ordered](data []E, a, b int) bool {
   200  	const (
   201  		maxSteps         = 5  // maximum number of adjacent out-of-order pairs that will get shifted
   202  		shortestShifting = 50 // don't shift any elements on short arrays
   203  	)
   204  	i := a + 1
   205  	for j := 0; j < maxSteps; j++ {
   206  		for i < b && !(data[i] < data[i-1]) {
   207  			i++
   208  		}
   209  
   210  		if i == b {
   211  			return true
   212  		}
   213  
   214  		if b-a < shortestShifting {
   215  			return false
   216  		}
   217  
   218  		data[i], data[i-1] = data[i-1], data[i]
   219  
   220  		// Shift the smaller one to the left.
   221  		if i-a >= 2 {
   222  			for j := i - 1; j >= 1; j-- {
   223  				if !(data[j] < data[j-1]) {
   224  					break
   225  				}
   226  				data[j], data[j-1] = data[j-1], data[j]
   227  			}
   228  		}
   229  		// Shift the greater one to the right.
   230  		if b-i >= 2 {
   231  			for j := i + 1; j < b; j++ {
   232  				if !(data[j] < data[j-1]) {
   233  					break
   234  				}
   235  				data[j], data[j-1] = data[j-1], data[j]
   236  			}
   237  		}
   238  	}
   239  	return false
   240  }
   241  
   242  // breakPatternsOrdered scatters some elements around in an attempt to break some patterns
   243  // that might cause imbalanced partitions in quicksort.
   244  func breakPatternsOrdered[E btype.Ordered](data []E, a, b int) {
   245  	length := b - a
   246  	if length >= 8 {
   247  		random := xorshift(length)
   248  		modulus := nextPowerOfTwo(length)
   249  
   250  		for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
   251  			other := int(uint(random.Next()) & (modulus - 1))
   252  			if other >= length {
   253  				other -= length
   254  			}
   255  			data[idx], data[a+other] = data[a+other], data[idx]
   256  		}
   257  	}
   258  }
   259  
   260  // choosePivotOrdered chooses a pivot in data[a:b].
   261  //
   262  // [0,8): chooses a static pivot.
   263  // [8,shortestNinther): uses the simple median-of-three method.
   264  // [shortestNinther,∞): uses the Tukey ninther method.
   265  func choosePivotOrdered[E btype.Ordered](data []E, a, b int) (pivot int, hint sortedHint) {
   266  	const (
   267  		shortestNinther = 50
   268  		maxSwaps        = 4 * 3
   269  	)
   270  
   271  	l := b - a
   272  
   273  	var (
   274  		swaps int
   275  		i     = a + l/4*1
   276  		j     = a + l/4*2
   277  		k     = a + l/4*3
   278  	)
   279  
   280  	if l >= 8 {
   281  		if l >= shortestNinther {
   282  			// Tukey ninther method, the idea came from Rust's implementation.
   283  			i = medianAdjacentOrdered(data, i, &swaps)
   284  			j = medianAdjacentOrdered(data, j, &swaps)
   285  			k = medianAdjacentOrdered(data, k, &swaps)
   286  		}
   287  		// Find the median among i, j, k and stores it into j.
   288  		j = medianOrdered(data, i, j, k, &swaps)
   289  	}
   290  
   291  	switch swaps {
   292  	case 0:
   293  		return j, increasingHint
   294  	case maxSwaps:
   295  		return j, decreasingHint
   296  	default:
   297  		return j, unknownHint
   298  	}
   299  }
   300  
   301  // order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
   302  func order2Ordered[E btype.Ordered](data []E, a, b int, swaps *int) (int, int) {
   303  	if data[b] < data[a] {
   304  		*swaps++
   305  		return b, a
   306  	}
   307  	return a, b
   308  }
   309  
   310  // medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
   311  func medianOrdered[E btype.Ordered](data []E, a, b, c int, swaps *int) int {
   312  	a, b = order2Ordered(data, a, b, swaps)
   313  	b, c = order2Ordered(data, b, c, swaps)
   314  	a, b = order2Ordered(data, a, b, swaps)
   315  	return b
   316  }
   317  
   318  // medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
   319  func medianAdjacentOrdered[E btype.Ordered](data []E, a int, swaps *int) int {
   320  	return medianOrdered(data, a-1, a, a+1, swaps)
   321  }
   322  
   323  func reverseRangeOrdered[E btype.Ordered](data []E, a, b int) {
   324  	i := a
   325  	j := b - 1
   326  	for i < j {
   327  		data[i], data[j] = data[j], data[i]
   328  		i++
   329  		j--
   330  	}
   331  }
   332  
   333  func swapRangeOrdered[E btype.Ordered](data []E, a, b, n int) {
   334  	for i := 0; i < n; i++ {
   335  		data[a+i], data[b+i] = data[b+i], data[a+i]
   336  	}
   337  }
   338  
   339  func stableOrdered[E btype.Ordered](data []E, n int) {
   340  	blockSize := 20 // must be > 0
   341  	a, b := 0, blockSize
   342  	for b <= n {
   343  		insertionSortOrdered(data, a, b)
   344  		a = b
   345  		b += blockSize
   346  	}
   347  	insertionSortOrdered(data, a, n)
   348  
   349  	for blockSize < n {
   350  		a, b = 0, 2*blockSize
   351  		for b <= n {
   352  			symMergeOrdered(data, a, a+blockSize, b)
   353  			a = b
   354  			b += 2 * blockSize
   355  		}
   356  		if m := a + blockSize; m < n {
   357  			symMergeOrdered(data, a, m, n)
   358  		}
   359  		blockSize *= 2
   360  	}
   361  }
   362  
   363  // symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using
   364  // the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
   365  // Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
   366  // Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
   367  // Computer Science, pages 714-723. Springer, 2004.
   368  //
   369  // Let M = m-a and N = b-n. Wolog M < N.
   370  // The recursion depth is bound by ceil(log(N+M)).
   371  // The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
   372  // The algorithm needs O((M+N)*log(M)) calls to data.Swap.
   373  //
   374  // The paper gives O((M+N)*log(M)) as the number of assignments assuming a
   375  // rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
   376  // in the paper carries through for Swap operations, especially as the block
   377  // swapping rotate uses only O(M+N) Swaps.
   378  //
   379  // symMerge assumes non-degenerate arguments: a < m && m < b.
   380  // Having the caller check this condition eliminates many leaf recursion calls,
   381  // which improves performance.
   382  func symMergeOrdered[E btype.Ordered](data []E, a, m, b int) {
   383  	// Avoid unnecessary recursions of symMerge
   384  	// by direct insertion of data[a] into data[m:b]
   385  	// if data[a:m] only contains one element.
   386  	if m-a == 1 {
   387  		// Use binary search to find the lowest index i
   388  		// such that data[i] >= data[a] for m <= i < b.
   389  		// Exit the search loop with i == b in case no such index exists.
   390  		i := m
   391  		j := b
   392  		for i < j {
   393  			h := int(uint(i+j) >> 1)
   394  			if data[h] < data[a] {
   395  				i = h + 1
   396  			} else {
   397  				j = h
   398  			}
   399  		}
   400  		// Swap values until data[a] reaches the position before i.
   401  		for k := a; k < i-1; k++ {
   402  			data[k], data[k+1] = data[k+1], data[k]
   403  		}
   404  		return
   405  	}
   406  
   407  	// Avoid unnecessary recursions of symMerge
   408  	// by direct insertion of data[m] into data[a:m]
   409  	// if data[m:b] only contains one element.
   410  	if b-m == 1 {
   411  		// Use binary search to find the lowest index i
   412  		// such that data[i] > data[m] for a <= i < m.
   413  		// Exit the search loop with i == m in case no such index exists.
   414  		i := a
   415  		j := m
   416  		for i < j {
   417  			h := int(uint(i+j) >> 1)
   418  			if !(data[m] < data[h]) {
   419  				i = h + 1
   420  			} else {
   421  				j = h
   422  			}
   423  		}
   424  		// Swap values until data[m] reaches the position i.
   425  		for k := m; k > i; k-- {
   426  			data[k], data[k-1] = data[k-1], data[k]
   427  		}
   428  		return
   429  	}
   430  
   431  	mid := int(uint(a+b) >> 1)
   432  	n := mid + m
   433  	var start, r int
   434  	if m > mid {
   435  		start = n - b
   436  		r = mid
   437  	} else {
   438  		start = a
   439  		r = m
   440  	}
   441  	p := n - 1
   442  
   443  	for start < r {
   444  		c := int(uint(start+r) >> 1)
   445  		if !(data[p-c] < data[c]) {
   446  			start = c + 1
   447  		} else {
   448  			r = c
   449  		}
   450  	}
   451  
   452  	end := n - start
   453  	if start < m && m < end {
   454  		rotateOrdered(data, start, m, end)
   455  	}
   456  	if a < start && start < mid {
   457  		symMergeOrdered(data, a, start, mid)
   458  	}
   459  	if mid < end && end < b {
   460  		symMergeOrdered(data, mid, end, b)
   461  	}
   462  }
   463  
   464  // rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
   465  // Data of the form 'x u v y' is changed to 'x v u y'.
   466  // rotate performs at most b-a many calls to data.Swap,
   467  // and it assumes non-degenerate arguments: a < m && m < b.
   468  func rotateOrdered[E btype.Ordered](data []E, a, m, b int) {
   469  	i := m - a
   470  	j := b - m
   471  
   472  	for i != j {
   473  		if i > j {
   474  			swapRangeOrdered(data, m-i, m, j)
   475  			i -= j
   476  		} else {
   477  			swapRangeOrdered(data, m-i, m+j-i, i)
   478  			j -= i
   479  		}
   480  	}
   481  	// i == j
   482  	swapRangeOrdered(data, m-i, m, i)
   483  }