github.com/puellanivis/breton@v0.2.16/lib/sort/radix.go (about)

     1  package sort
     2  
     3  import (
     4  	"sort"
     5  	_ "unsafe" // this is to explicitly signal this file is unsafe.
     6  )
     7  
     8  // RadixTest defines a function that Returns true if the i'th element of the sort.RadixInterface is set
     9  type RadixTest func(i int) bool
    10  
    11  // RadixInterface defines the functions necessary for Radix to use a radix sort rather than sort.Sort.
    12  type RadixInterface interface {
    13  	Interface
    14  
    15  	// Returns start, and end of radix values to run through for RadixFunc
    16  	RadixRange() (int, int)
    17  	RadixFunc(r int) RadixTest
    18  }
    19  
    20  // Radix attempts to perform a radix sort on the given argument.
    21  //
    22  // If the argument does not implement RadixInterface, or is not a builtin basic slice,
    23  // but it implements sort.Interface, then sort.Sort will be called on the argument.
    24  func Radix(a interface{}) {
    25  	if a == nil {
    26  		return
    27  	}
    28  
    29  	switch a := a.(type) {
    30  	case RadixInterface:
    31  		radix(a)
    32  
    33  	case []uint:
    34  		radix(UintSlice(a))
    35  	case []uint8:
    36  		radix(Uint8Slice(a))
    37  	case []uint16:
    38  		radix(Uint16Slice(a))
    39  	case []uint32:
    40  		radix(Uint32Slice(a))
    41  	case []uint64:
    42  		radix(Uint64Slice(a))
    43  
    44  	case []int:
    45  		radix(IntSlice(a))
    46  	case []int8:
    47  		radix(Int8Slice(a))
    48  	case []int16:
    49  		radix(Int16Slice(a))
    50  	case []int32:
    51  		radix(Int32Slice(a))
    52  	case []int64:
    53  		radix(Int64Slice(a))
    54  
    55  	case []float64:
    56  		radix(Float64Slice(a))
    57  	case []float32:
    58  		radix(Float32Slice(a))
    59  
    60  	case []string:
    61  		radix(StringSlice(a))
    62  
    63  	default:
    64  		// fallback: use the builtin sort.Sort
    65  		sort.Sort(a.(sort.Interface))
    66  	}
    67  }
    68  
    69  func radix(a RadixInterface) {
    70  	s, e := a.RadixRange()
    71  	quickRadix(a, 0, a.Len(), s, e+1)
    72  }
    73  
    74  func sortTwo(a RadixInterface, i int) {
    75  	if a.Less(i+1, i) {
    76  		a.Swap(i, i+1)
    77  	}
    78  }
    79  
    80  func quickRadix(a RadixInterface, start, end, radix, last int) {
    81  	r := end - start
    82  	if r < 3 {
    83  		if r == 2 {
    84  			sortTwo(a, start)
    85  		}
    86  		return
    87  	}
    88  
    89  	if qsortInstead(r, radix, last) {
    90  		quickSort(a, start, end, maxDepth(r))
    91  		return
    92  	}
    93  
    94  	radixSort(a, start, end, radix, last)
    95  }
    96  
    97  type swapFunc func(i, j int)
    98  
    99  func radixPass(f RadixTest, swap swapFunc, start, end int) (pivot int) {
   100  	i, j := start, end-1
   101  
   102  	for i < j {
   103  		// from the start, find the i-th item that satisfies radix.
   104  		for i < j && !f(i) {
   105  			i++
   106  		}
   107  
   108  		// from the end, find the j-th item that doesn’t satisfy radix.
   109  		for i < j && f(j) {
   110  			j--
   111  		}
   112  
   113  		if j <= i {
   114  			// avoid swapping if they’ve passed each other, or are the same thing…
   115  			// while the swap is no big deal, the extra increments not good
   116  			break
   117  		}
   118  
   119  		swap(i, j)
   120  		i++
   121  		j--
   122  	}
   123  
   124  	// we’re standing on a pivot, if it doesn’t satisfy pivot, then pivot just after.
   125  	if i == j && !f(i) {
   126  		i++
   127  	}
   128  
   129  	return i
   130  }
   131  
   132  func radixSort(a RadixInterface, start, end, radix, last int) {
   133  	for radix < last {
   134  		r := end - start
   135  		if r < 3 {
   136  			if r == 2 {
   137  				sortTwo(a, start)
   138  			}
   139  			return
   140  		}
   141  
   142  		if qsortInstead(r, radix, last) {
   143  			quickSort(a, start, end, maxDepth(r))
   144  			return
   145  		}
   146  
   147  		pivot := radixPass(a.RadixFunc(radix), a.Swap, start, end)
   148  
   149  		radix++
   150  
   151  		if pivot-start < end-pivot {
   152  			quickRadix(a, start, pivot, radix, last)
   153  			start = pivot
   154  
   155  		} else {
   156  			quickRadix(a, pivot, end, radix, last)
   157  			end = pivot
   158  		}
   159  	}
   160  }