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

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package bslice
     6  
     7  import (
     8  	"math"
     9  	"math/rand"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
    17  var float64s = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8, 74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3}
    18  var float64sWithNaNs = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
    19  var strs = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
    20  
    21  func TestSortIntSlice(t *testing.T) {
    22  	data := Clone(ints[:])
    23  	Sort(data)
    24  	if !IsSorted(data) {
    25  		t.Errorf("sorted %v", ints)
    26  		t.Errorf("   got %v", data)
    27  	}
    28  }
    29  
    30  func TestSortFuncIntSlice(t *testing.T) {
    31  	data := Clone(ints[:])
    32  	SortFunc(data, func(a, b int) bool { return a < b })
    33  	if !IsSorted(data) {
    34  		t.Errorf("sorted %v", ints)
    35  		t.Errorf("   got %v", data)
    36  	}
    37  }
    38  
    39  func TestSortFloat64Slice(t *testing.T) {
    40  	data := Clone(float64s[:])
    41  	Sort(data)
    42  	if !IsSorted(data) {
    43  		t.Errorf("sorted %v", float64s)
    44  		t.Errorf("   got %v", data)
    45  	}
    46  }
    47  
    48  func TestSortFloat64SliceWithNaNs(t *testing.T) {
    49  	data := float64sWithNaNs[:]
    50  	input := Clone(data)
    51  
    52  	// Make sure Sort doesn't panic when the slice contains NaNs.
    53  	Sort(data)
    54  	// Check whether the result is a permutation of the input.
    55  	sort.Float64s(data)
    56  	sort.Float64s(input)
    57  	for i, v := range input {
    58  		if data[i] != v && !(math.IsNaN(data[i]) && math.IsNaN(v)) {
    59  			t.Fatalf("the result is not a permutation of the input\ngot %v\nwant %v", data, input)
    60  		}
    61  	}
    62  }
    63  
    64  func TestSortStringSlice(t *testing.T) {
    65  	data := Clone(strs[:])
    66  	Sort(data)
    67  	if !IsSorted(data) {
    68  		t.Errorf("sorted %v", strs)
    69  		t.Errorf("   got %v", data)
    70  	}
    71  }
    72  
    73  func TestSortLarge_Random(t *testing.T) {
    74  	n := 1000000
    75  	if testing.Short() {
    76  		n /= 100
    77  	}
    78  	data := make([]int, n)
    79  	for i := 0; i < len(data); i++ {
    80  		data[i] = rand.Intn(100)
    81  	}
    82  	if IsSorted(data) {
    83  		t.Fatalf("terrible rand.rand")
    84  	}
    85  	Sort(data)
    86  	if !IsSorted(data) {
    87  		t.Errorf("sort didn't sort - 1M ints")
    88  	}
    89  }
    90  
    91  type intPair struct {
    92  	a, b int
    93  }
    94  
    95  type intPairs []intPair
    96  
    97  // Pairs compare on a only.
    98  func intPairLess(x, y intPair) bool {
    99  	return x.a < y.a
   100  }
   101  
   102  // Record initial order in B.
   103  func (d intPairs) initB() {
   104  	for i := range d {
   105  		d[i].b = i
   106  	}
   107  }
   108  
   109  // InOrder checks if a-equal elements were not reordered.
   110  func (d intPairs) inOrder() bool {
   111  	lastA, lastB := -1, 0
   112  	for i := 0; i < len(d); i++ {
   113  		if lastA != d[i].a {
   114  			lastA = d[i].a
   115  			lastB = d[i].b
   116  			continue
   117  		}
   118  		if d[i].b <= lastB {
   119  			return false
   120  		}
   121  		lastB = d[i].b
   122  	}
   123  	return true
   124  }
   125  
   126  func TestStability(t *testing.T) {
   127  	n, m := 100000, 1000
   128  	if testing.Short() {
   129  		n, m = 1000, 100
   130  	}
   131  	data := make(intPairs, n)
   132  
   133  	// random distribution
   134  	for i := 0; i < len(data); i++ {
   135  		data[i].a = rand.Intn(m)
   136  	}
   137  	if IsSortedFunc(data, intPairLess) {
   138  		t.Fatalf("terrible rand.rand")
   139  	}
   140  	data.initB()
   141  	SortStableFunc(data, intPairLess)
   142  	if !IsSortedFunc(data, intPairLess) {
   143  		t.Errorf("Stable didn't sort %d ints", n)
   144  	}
   145  	if !data.inOrder() {
   146  		t.Errorf("Stable wasn't stable on %d ints", n)
   147  	}
   148  
   149  	// already sorted
   150  	data.initB()
   151  	SortStableFunc(data, intPairLess)
   152  	if !IsSortedFunc(data, intPairLess) {
   153  		t.Errorf("Stable shuffled sorted %d ints (order)", n)
   154  	}
   155  	if !data.inOrder() {
   156  		t.Errorf("Stable shuffled sorted %d ints (stability)", n)
   157  	}
   158  
   159  	// sorted reversed
   160  	for i := 0; i < len(data); i++ {
   161  		data[i].a = len(data) - i
   162  	}
   163  	data.initB()
   164  	SortStableFunc(data, intPairLess)
   165  	if !IsSortedFunc(data, intPairLess) {
   166  		t.Errorf("Stable didn't sort %d ints", n)
   167  	}
   168  	if !data.inOrder() {
   169  		t.Errorf("Stable wasn't stable on %d ints", n)
   170  	}
   171  }
   172  
   173  func TestBinarySearch(t *testing.T) {
   174  	str1 := []string{"foo"}
   175  	str2 := []string{"ab", "ca"}
   176  	str3 := []string{"mo", "qo", "vo"}
   177  	str4 := []string{"ab", "ad", "ca", "xy"}
   178  
   179  	// slice with repeating elements
   180  	strRepeats := []string{"ba", "ca", "da", "da", "da", "ka", "ma", "ma", "ta"}
   181  
   182  	// slice with all element equal
   183  	strSame := []string{"xx", "xx", "xx"}
   184  
   185  	tests := []struct {
   186  		data      []string
   187  		target    string
   188  		wantPos   int
   189  		wantFound bool
   190  	}{
   191  		{[]string{}, "foo", 0, false},
   192  		{[]string{}, "", 0, false},
   193  
   194  		{str1, "foo", 0, true},
   195  		{str1, "bar", 0, false},
   196  		{str1, "zx", 1, false},
   197  
   198  		{str2, "aa", 0, false},
   199  		{str2, "ab", 0, true},
   200  		{str2, "ad", 1, false},
   201  		{str2, "ca", 1, true},
   202  		{str2, "ra", 2, false},
   203  
   204  		{str3, "bb", 0, false},
   205  		{str3, "mo", 0, true},
   206  		{str3, "nb", 1, false},
   207  		{str3, "qo", 1, true},
   208  		{str3, "tr", 2, false},
   209  		{str3, "vo", 2, true},
   210  		{str3, "xr", 3, false},
   211  
   212  		{str4, "aa", 0, false},
   213  		{str4, "ab", 0, true},
   214  		{str4, "ac", 1, false},
   215  		{str4, "ad", 1, true},
   216  		{str4, "ax", 2, false},
   217  		{str4, "ca", 2, true},
   218  		{str4, "cc", 3, false},
   219  		{str4, "dd", 3, false},
   220  		{str4, "xy", 3, true},
   221  		{str4, "zz", 4, false},
   222  
   223  		{strRepeats, "da", 2, true},
   224  		{strRepeats, "db", 5, false},
   225  		{strRepeats, "ma", 6, true},
   226  		{strRepeats, "mb", 8, false},
   227  
   228  		{strSame, "xx", 0, true},
   229  		{strSame, "ab", 0, false},
   230  		{strSame, "zz", 3, false},
   231  	}
   232  	for _, tt := range tests {
   233  		t.Run(tt.target, func(t *testing.T) {
   234  			{
   235  				pos, found := BinarySearch(tt.data, tt.target)
   236  				if pos != tt.wantPos || found != tt.wantFound {
   237  					t.Errorf("BinarySearch got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound)
   238  				}
   239  			}
   240  
   241  			{
   242  				pos, found := BinarySearchFunc(tt.data, tt.target, strings.Compare)
   243  				if pos != tt.wantPos || found != tt.wantFound {
   244  					t.Errorf("BinarySearchFunc got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound)
   245  				}
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  func TestBinarySearchInts(t *testing.T) {
   252  	data := []int{20, 30, 40, 50, 60, 70, 80, 90}
   253  	tests := []struct {
   254  		target    int
   255  		wantPos   int
   256  		wantFound bool
   257  	}{
   258  		{20, 0, true},
   259  		{23, 1, false},
   260  		{43, 3, false},
   261  		{80, 6, true},
   262  	}
   263  	for _, tt := range tests {
   264  		t.Run(strconv.Itoa(tt.target), func(t *testing.T) {
   265  			{
   266  				pos, found := BinarySearch(data, tt.target)
   267  				if pos != tt.wantPos || found != tt.wantFound {
   268  					t.Errorf("BinarySearch got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound)
   269  				}
   270  			}
   271  
   272  			{
   273  				cmp := func(a, b int) int {
   274  					return a - b
   275  				}
   276  				pos, found := BinarySearchFunc(data, tt.target, cmp)
   277  				if pos != tt.wantPos || found != tt.wantFound {
   278  					t.Errorf("BinarySearchFunc got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound)
   279  				}
   280  			}
   281  		})
   282  	}
   283  }
   284  
   285  func TestBinarySearchFunc(t *testing.T) {
   286  	data := []int{1, 10, 11, 2} // sorted lexicographically
   287  	cmp := func(a int, b string) int {
   288  		return strings.Compare(strconv.Itoa(a), b)
   289  	}
   290  	pos, found := BinarySearchFunc(data, "2", cmp)
   291  	if pos != 3 || !found {
   292  		t.Errorf("BinarySearchFunc(%v, %q, cmp) = %v, %v, want %v, %v", data, "2", pos, found, 3, true)
   293  	}
   294  }