(module bisect-search mzscheme
(require (lib "list.ss")
(lib "etc.ss")
(lib "contract.ss"))
(provide/contract (vector-bisect-left ((vectorof any/c) (any/c any/c . -> . (integer-in -1 1)) any/c . -> . number?)))
(define vector-bisect-left
(case-lambda
[(vec cmp-function key)
(vector-bisect-left vec cmp-function key 0 (vector-length vec))]
[(vec cmp-function key left right)
(let loop ([left left]
[right right])
(cond
[(>= left right) left]
[else
(let* ([m (midpoint left right)]
[cmp (cmp-function key (vector-ref vec m))])
(cond
[(< cmp 0)
(loop left m)]
[(> cmp 0)
(loop (add1 m) right)]
[else
(loop left m)]))]))]))
(provide/contract (vector-bisect-right ((vectorof any/c) (any/c any/c . -> . (integer-in -1 1)) any/c . -> . number?)))
(define vector-bisect-right
(case-lambda
[(vec cmp-function key)
(vector-bisect-right vec cmp-function key 0 (vector-length vec))]
[(vec cmp-function key left right)
(let loop ([left left]
[right right])
(cond
[(>= left right) left]
[else
(let* ([m (midpoint left right)]
[cmp (cmp-function key (vector-ref vec m))])
(cond
[(< cmp 0)
(loop left m)]
[(> cmp 0)
(loop (add1 m) right)]
[else
(loop (add1 m) right)]))]))]))
(define (midpoint a b)
(quotient (+ a b) 2))
(define (make-cmp lt eq)
(lambda (x y)
(cond [(lt x y) -1]
[(eq x y) 0]
[else 1])))
(define numeric-cmp (make-cmp < =))
(define (vector-search-left vec cmp-function key)
(let loop ([i 0])
(cond
[(= i (vector-length vec))
i]
[else
(let ([v (cmp-function key (vector-ref vec i))])
(cond
[( < v 0) i]
[( = v 0) i]
[else
(loop (add1 i))]))])))
(define (vector-search-right vec cmp-function key)
(let loop ([i 0])
(cond
[(= i (vector-length vec))
i]
[else
(let ([v (cmp-function key (vector-ref vec i))])
(cond
[( < v 0)
i]
[( = v 0)
(loop (add1 i))]
[else
(loop (add1 i))]))])))
(define (test-left v cmp key)
(test vector-bisect-left vector-search-left v cmp key))
(define (test-right v cmp key)
(test vector-bisect-right vector-search-right v cmp key))
(define (test bisect-f linear-f v cmp key)
(let ([binary (bisect-f v cmp key)]
[linear (linear-f v cmp key)])
(unless (= binary linear)
(error 'test
"vec:~a key:~a bisect:~a linear:~a"
v
key
binary
linear))))
(define-syntax (repeat stx)
(syntax-case stx ()
[(_ (var n) e ...)
(syntax/loc stx
(let loop ([var 0])
(when (< var n)
e ...
(loop (add1 var)))))]))
(define (make-test-vector number-of-elts range)
(list->vector
(quicksort (build-list number-of-elts
(lambda (i) (random range)))
<)))
(define (random-monkey-test-suite N)
(let ([vec (make-test-vector N N)])
(repeat (j N)
(test-right vec numeric-cmp j)
(test-left vec numeric-cmp j))))
(define (grade score)
(define grades "FEDCBA")
(define breakpoints (vector 30 44 66 75 85))
(string-ref
grades
(vector-bisect-right breakpoints numeric-cmp score))))