]> Cypherpunks repositories - gostls13.git/commitdiff
sort: invert meaning of f in Search
authorRuss Cox <rsc@golang.org>
Thu, 18 Nov 2010 16:46:07 +0000 (11:46 -0500)
committerRuss Cox <rsc@golang.org>
Thu, 18 Nov 2010 16:46:07 +0000 (11:46 -0500)
Backwards incompatible change, but makes
it easier to reason about non-idiomatic searches:
now f specifies what is sought.

R=gri
CC=golang-dev
https://golang.org/cl/3195042

src/pkg/go/token/position.go
src/pkg/sort/search.go
src/pkg/sort/search_test.go

index 03c353d136b657cf97a182a517a1f4669ca0cd11..716a71beae20948dbc88a95caf028936ab2986ea 100644 (file)
@@ -94,7 +94,7 @@ func (p Pos) IsValid() bool {
 
 
 func searchFiles(a []*File, x int) int {
-       return sort.Search(len(a), func(i int) bool { return a[i].base <= x }) - 1
+       return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
 }
 
 
@@ -252,12 +252,12 @@ func (f *File) Position(offset int) Position {
 
 
 func searchUints(a []int, x int) int {
-       return sort.Search(len(a), func(i int) bool { return a[i] <= x }) - 1
+       return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
 }
 
 
 func searchLineInfos(a []lineInfo, x int) int {
-       return sort.Search(len(a), func(i int) bool { return a[i].offset <= x }) - 1
+       return sort.Search(len(a), func(i int) bool { return a[i].offset > x }) - 1
 }
 
 
index b573ad175273061d75b9054124e7f9ed4029edb9..b3ddd2dfa8155ce9f641eeb1959068b19ba67761 100644 (file)
@@ -7,11 +7,11 @@
 package sort
 
 // Search uses binary search to find and return the smallest index i
-// in [0, n) at which f(i) is false, assuming that on the range [0, n), 
-// f(i) == false implies f(i+1) == false.  That is, Search requires that
-// f is true for some (possibly empty) prefix of the input range [0, n)
-// and then false for the (possibly empty) remainder; Search returns
-// the first false index.  If there is no such index, Search returns n.
+// in [0, n) at which f(i) is true, assuming that on the range [0, n), 
+// f(i) == true implies f(i+1) == true.  That is, Search requires that
+// f is false for some (possibly empty) prefix of the input range [0, n)
+// and then true for the (possibly empty) remainder; Search returns
+// the first true index.  If there is no such index, Search returns n.
 // Search calls f(i) only for i in the range [0, n).
 //
 // A common use of Search is to find the index i for a value x in
@@ -21,19 +21,19 @@ package sort
 // ordered.
 //
 // For instance, given a slice data sorted in ascending order,
-// the call Search(len(data), func(i int) bool { return data[i] < 23 })
+// the call Search(len(data), func(i int) bool { return data[i] >= 23 })
 // returns the smallest index i such that data[i] >= 23.  If the caller
 // wants to find whether 23 is in the slice, it must test data[i] == 23
 // separately.
 //
-// Searching data sorted in descending order would use the >
-// operator instead of the < operator.
+// Searching data sorted in descending order would use the <=
+// operator instead of the >= operator.
 //
 // To complete the example above, the following code tries to find the value
 // x in an integer slice data sorted in ascending order:
 //
 //     x := 23
-//     i := sort.Search(len(data), func(i int) bool { return data[i] < x })
+//     i := sort.Search(len(data), func(i int) bool { return data[i] >= x })
 //     if i < len(data) && data[i] == x {
 //             // x is present at data[i]
 //     } else {
@@ -47,7 +47,7 @@ package sort
 //             var s string
 //             fmt.Printf("Pick an integer from 0 to 100.\n")
 //             answer := sort.Search(100, func(i int) bool {
-//                     fmt.Printf("Is your number > %d? ", i)
+//                     fmt.Printf("Is your number <= %d? ", i)
 //                     fmt.Scanf("%s", &s)
 //                     return s != "" && s[0] == 'y'
 //             })
@@ -55,19 +55,19 @@ package sort
 //     }
 //
 func Search(n int, f func(int) bool) int {
-       // Define f(-1) == true and f(n) == false.
-       // Invariant: f(i-1) == true, f(j) == false.
+       // Define f(-1) == false and f(n) == true.
+       // Invariant: f(i-1) == false, f(j) == true.
        i, j := 0, n
        for i < j {
                h := i + (j-i)/2 // avoid overflow when computing h
                // i ≤ h < j
-               if f(h) {
-                       i = h + 1 // preserves f(i-1) == true
+               if !f(h) {
+                       i = h + 1 // preserves f(i-1) == false
                } else {
-                       j = h // preserves f(j) == false
+                       j = h // preserves f(j) == true
                }
        }
-       // i == j, f(i-1) == true, and f(j) (= f(i)) == false  =>  answer is i.
+       // i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
        return i
 }
 
@@ -78,7 +78,7 @@ func Search(n int, f func(int) bool) int {
 // as specified by Search. The array must be sorted in ascending order.
 //
 func SearchInts(a []int, x int) int {
-       return Search(len(a), func(i int) bool { return a[i] < x })
+       return Search(len(a), func(i int) bool { return a[i] >= x })
 }
 
 
@@ -86,7 +86,7 @@ func SearchInts(a []int, x int) int {
 // as specified by Search. The array must be sorted in ascending order.
 // 
 func SearchFloats(a []float, x float) int {
-       return Search(len(a), func(i int) bool { return a[i] < x })
+       return Search(len(a), func(i int) bool { return a[i] >= x })
 }
 
 
@@ -94,7 +94,7 @@ func SearchFloats(a []float, x float) int {
 // as specified by Search. The array must be sorted in ascending order.
 // 
 func SearchStrings(a []string, x string) int {
-       return Search(len(a), func(i int) bool { return a[i] < x })
+       return Search(len(a), func(i int) bool { return a[i] >= x })
 }
 
 
index 10dbd3a75abbdad9c22a2d991ed904ae44c7aaa4..e16e2c93fb7878027274716bdb26d77e89b51be2 100644 (file)
@@ -9,7 +9,7 @@ import "testing"
 
 func f(a []int, x int) func(int) bool {
        return func(i int) bool {
-               return a[i] < x
+               return a[i] >= x
        }
 }
 
@@ -23,12 +23,12 @@ var tests = []struct {
        i    int
 }{
        {"empty", 0, nil, 0},
-       {"1 1", 1, func(i int) bool { return i < 1 }, 1},
-       {"1 false", 1, func(i int) bool { return false }, 0},
-       {"1 true", 1, func(i int) bool { return true }, 1},
-       {"1e9 991", 1e9, func(i int) bool { return i < 991 }, 991},
-       {"1e9 false", 1e9, func(i int) bool { return false }, 0},
-       {"1e9 true", 1e9, func(i int) bool { return true }, 1e9},
+       {"1 1", 1, func(i int) bool { return i >= 1 }, 1},
+       {"1 true", 1, func(i int) bool { return true }, 0},
+       {"1 false", 1, func(i int) bool { return false }, 1},
+       {"1e9 991", 1e9, func(i int) bool { return i >= 991 }, 991},
+       {"1e9 true", 1e9, func(i int) bool { return true }, 0},
+       {"1e9 false", 1e9, func(i int) bool { return false }, 1e9},
        {"data -20", len(data), f(data, -20), 0},
        {"data -10", len(data), f(data, -10), 0},
        {"data -9", len(data), f(data, -9), 1},
@@ -41,8 +41,9 @@ var tests = []struct {
        {"data 101", len(data), f(data, 101), 12},
        {"data 10000", len(data), f(data, 10000), 13},
        {"data 10001", len(data), f(data, 10001), 14},
-       {"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] > 7 }, 4},
-       {"descending 7", 1e9, func(i int) bool { return 1e9-i > 7 }, 1e9 - 7},
+       {"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] <= 7 }, 4},
+       {"descending 7", 1e9, func(i int) bool { return 1e9-i <= 7 }, 1e9 - 7},
+       {"overflow", 2e9, func(i int) bool { return false }, 2e9},
 }
 
 
@@ -79,7 +80,7 @@ func TestSearchEfficiency(t *testing.T) {
                max := log2(n)
                for x := 0; x < n; x += step {
                        count := 0
-                       i := Search(n, func(i int) bool { count++; return i < x })
+                       i := Search(n, func(i int) bool { count++; return i >= x })
                        if i != x {
                                t.Errorf("n = %d: expected index %d; got %d", n, x, i)
                        }
@@ -119,3 +120,18 @@ func TestSearchWrappers(t *testing.T) {
                }
        }
 }
+
+
+// Abstract exhaustive test: all sizes up to 100,
+// all possible return values.  If there are any small
+// corner cases, this test exercises them.
+func TestSearchExhaustive(t *testing.T) {
+       for size := 0; size <= 100; size++ {
+               for targ := 0; targ <= size; targ++ {
+                       i := Search(size, func(i int) bool { return i >= targ })
+                       if i != targ {
+                               t.Errorf("Search(%d, %d) = %d", size, targ, i)
+                       }
+               }
+       }
+}