binary-search-2.0.0/0000755000000000000000000000000007346545000012431 5ustar0000000000000000binary-search-2.0.0/LICENSE0000644000000000000000000000254107346545000013440 0ustar0000000000000000Copyright 2008, Ross Paterson. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. binary-search-2.0.0/Numeric/0000755000000000000000000000000007346545000014033 5ustar0000000000000000binary-search-2.0.0/Numeric/Search.hs0000644000000000000000000003057107346545000015602 0ustar0000000000000000{-# LANGUAGE DeriveFunctor, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, MultiWayIf, RecordWildCards, ScopedTypeVariables, TupleSections #-} -- | This package provides combinators to construct many variants of -- binary search. Most generally, it provides the binary search over -- predicate of the form @('Eq' b, 'Monad' m) => a -> m b@. The other -- searches are derived as special cases of this function. -- -- 'BinarySearch' assumes two things; -- -- 1. @b@, the codomain of 'PredicateM' belongs to type class 'Eq'. -- -- 2. Each value of @b@ form a convex set in the codomain space of the -- PredicateM. That is, if for certain pair @(left, right) :: (a, a)@ -- satisfies @pred left == val && pred right == val@, then also @pred -- x == val@ for all @x@ such that @left <= x <= right@. -- -- __Example 1.__ Find the approximate square root of 3. -- -- >>> largest True $ search positiveExponential divForever (\x -> x^2 < 3000000) -- Just 1732 -- >>> smallest False $ search positiveExponential divForever (\x -> x^2 < 3000000) -- Just 1733 -- >>> largest True $ search positiveExponential divideForever (\x -> x^2 < (3::Double)) -- Just 1.7320508075688772 -- >>> largest True $ search positiveExponential (divideTill 0.125) (\x -> x^2 < (3::Double)) -- Just 1.625 -- >>> smallest False $ search positiveExponential (divideTill 0.125) (\x -> x^2 < (3::Double)) -- Just 1.75 -- -- Pay attention to use the appropriate exponential search combinator to set up the initial search range. -- For example, the following code works as expected: -- -- >>> largest True $ search nonNegativeExponential divideForever (\x -> x^2 < (0.5::Double)) -- Just 0.7071067811865475 -- -- But this one does not terminate: -- -- @ -- __> largest True $ search positiveExponential divideForever (\x -> x^2 < (0.5::Double))__ -- ... runs forever ... -- @ -- -- __Example 2.__ Find the range of integers whose quotinent 7 is equal to 6. -- -- This is an example of how we can 'search' for discete and multi-valued predicate. -- -- >>> smallest 6 $ search (fromTo 0 100) divForever (\x -> x `div` 7) -- Just 42 -- >>> largest 6 $ search (fromTo 0 100) divForever (\x -> x `div` 7) -- Just 48 -- -- __Example 3.__ Find the minimum size of the container that can fit three bars of length 4, -- and find an actual way to fit them. -- -- We will solve this using a satisfiability modulo theory (SMT) solver. Since we need to evoke 'IO' -- to call for the SMT solver, -- This is a usecase for a monadic binary search. -- -- >>> import Data.List (isPrefixOf) -- >>> :{ -- do -- -- x fits within the container -- let x ⊂ r = (0 .<= x &&& x .<= r-4) -- -- x and y does not collide -- let x ∅ y = (x+4 .<= y ) -- let contain3 :: Integer -> IO (Evidence () String) -- contain3 r' = do -- let r = fromInteger r' :: SInteger -- ret <- show <$> sat (\x y z -> bAnd [x ⊂ r, y ⊂ r, z ⊂ r, x ∅ y, y ∅ z]) -- if "Satisfiable" `isPrefixOf` ret -- then return $ Evidence ret -- else return $ CounterEvidence () -- Just sz <- smallest evidence <$> searchM positiveExponential divForever contain3 -- putStrLn $ "Size of the container: " ++ show sz -- Just msg <- evidenceForSmallest <$> searchM positiveExponential divForever contain3 -- putStrLn msg -- :} -- Size of the container: 12 -- Satisfiable. Model: -- s0 = 0 :: Integer -- s1 = 4 :: Integer -- s2 = 8 :: Integer module Numeric.Search where import Control.Applicative import Data.Functor.Identity import Data.Maybe (fromJust, listToMaybe) import Prelude hiding (init, pred) -- $setup -- All the doctests in this document assume: -- -- >>> :set -XFlexibleContexts -- >>> import Data.SBV -- * Evidence -- | The 'Evidence' datatype is similar to 'Either' , but differes in that all 'CounterEvidence' values are -- equal to each other, and all 'Evidence' values are also -- equal to each other. The 'Evidence' type is used to binary-searching for some predicate and meanwhile returning evidences for that. -- -- In other words, 'Evidence' is a 'Bool' with additional information why it is 'True' or 'False'. -- -- >>> Evidence "He owns the gun" == Evidence "He was at the scene" -- True -- >>> Evidence "He loved her" == CounterEvidence "He loved her" -- False data Evidence a b = CounterEvidence a | Evidence b deriving (Show, Read, Functor) instance Eq (Evidence b a) where CounterEvidence _ == CounterEvidence _ = True Evidence _ == Evidence _ = True _ == _ = False instance Ord (Evidence b a) where CounterEvidence _ `compare` CounterEvidence _ = EQ Evidence _ `compare` Evidence _ = EQ CounterEvidence _ `compare` Evidence _ = GT Evidence _ `compare` CounterEvidence _ = LT instance Applicative (Evidence e) where pure = Evidence CounterEvidence e <*> _ = CounterEvidence e Evidence f <*> r = fmap f r instance Monad (Evidence e) where return = Evidence CounterEvidence l >>= _ = CounterEvidence l Evidence r >>= k = k r -- | 'evidence' = 'Evidence' 'undefined'. We can use this combinator to look up for some 'Evidence', -- since all 'Evidence's are equal. evidence :: Evidence a b evidence = Evidence undefined -- | 'counterEvidence' = 'CounterEvidence' 'undefined'. We can use this combinator to look up for any 'CounterEvidence', -- since all 'CounterEvidence's are equal. counterEvidence :: Evidence a b counterEvidence = CounterEvidence undefined -- * Search range -- | The @Range k lo k' hi@ represents the search result that @pred x == k@ for @lo <= x <= hi@. -- The 'Range' type also holds the evidences for the lower and the upper boundary. data Range b a = Range {loKey :: b, loVal :: a, hiKey :: b, hiVal :: a} deriving (Show, Read, Eq, Ord) -- | The (possibly infinite) lists of candidates for lower and upper bounds from which the search may be started. type SearchRange a = ([a], [a]) -- | Set the lower and upper boundary from those available from the candidate lists. -- From the pair of list, the @initializeSearchM@ tries to find the first @(lo, hi)@ -- such that @pred lo /= pred hi@, by the breadth-first search. initializeSearchM :: (Monad m, Eq b)=> SearchRange a -> (a -> m b) -> m [Range b a] initializeSearchM (lo:los,hi:his) pred0 = do pLo <- pred0 lo pHi <- pred0 hi let pop (p,x, []) = return (p,x,[]) pop (p,_, x2:xs) = do p2 <- pred0 x2 return (p2, x2, xs) go pez1@(p1,x1,xs1) pez2@(p2,x2,xs2) | p1 /= p2 = return [Range p1 x1 p1 x1, Range p2 x2 p2 x2] | null xs1 && null xs2 = return [Range p1 x1 p2 x2] | otherwise = do pez1' <- pop pez1 pez2' <- pop pez2 go pez1' pez2' go (pLo, lo,los) (pHi, hi, his) initializeSearchM _ _ = return [] -- | Start binary search from between 'minBound' and 'maxBound'. minToMax :: Bounded a => SearchRange a minToMax = ([minBound], [maxBound]) -- | Start binary search from between the given pair of boundaries. fromTo :: a -> a -> SearchRange a fromTo x y= ([x], [y]) -- | Exponentially search for lower boundary from @[-1, -2, -4, -8, ...]@, upper boundary from @[0, 1, 2, 4, 8, ...]@. -- Move on to the binary search once the first @(lo, hi)@ is found -- such that @pred lo /= pred hi@. exponential :: Num a => SearchRange a exponential = (iterate (*2) (-1), 0 : iterate (*2) 1) -- | Lower boundary is 1, upper boundary is from @[2, 4, 8, 16, ...]@. positiveExponential :: Num a => SearchRange a positiveExponential = ([1], iterate (*2) 2) -- | Lower boundary is 0, search upper boundary is from @[1, 2, 4, 8, ...]@. nonNegativeExponential :: Num a => SearchRange a nonNegativeExponential = ([0], iterate (*2) 1) -- | Lower boundary is @[0.5, 0.25, 0.125, ...]@, upper boundary is from @[1, 2, 4, 8, ...]@. positiveFractionalExponential :: Fractional a => SearchRange a positiveFractionalExponential = (iterate (/2) 0.5, iterate (*2) 1) -- | Lower boundary is from @[-2, -4, -8, -16, ...]@, upper boundary is -1. negativeExponential :: Num a => SearchRange a negativeExponential = (iterate (*2) (-2), [-1]) -- | Lower boundary is from @[-1, -2, -4, -8, ...]@, upper boundary is -0. nonPositiveExponential :: Num a => SearchRange a nonPositiveExponential = (iterate (*2) (-1), [0]) -- | Lower boundary is @[-1, -2, -4, -8, ...]@, upper boundary is from @[-0.5, -0.25, -0.125, ...]@. negativeFractionalExponential :: Fractional a => SearchRange a negativeFractionalExponential = (iterate (*2) (-1), iterate (/2) (-0.5)) -- * Splitters -- | The type of function that returns a value between @(lo, hi)@ as long as one is available. type Splitter a = a -> a -> Maybe a -- | Perform split forever, until we cannot find a mid-value because @hi-lo < 2@. -- This splitter assumes that the arguments are Integral, and uses the `div` funtion. -- -- Note that our dividing algorithm always find the mid value for any @hi-lo >= 2@. -- -- >>> prove $ \x y -> (y .>= x+2 &&& x+2 .> x) ==> let z = (x+1) `sDiv` 2 + y `sDiv` 2 in x .< z &&& z .< (y::SInt32) -- Q.E.D. divForever :: Integral a => Splitter a divForever lo hi = let mid = (lo+1) `div` 2 + hi `div` 2 in if lo == mid || mid == hi then Nothing else Just mid -- | Perform splitting until @hi - lo <= eps@. divTill :: Integral a => a -> Splitter a divTill eps lo hi | hi - lo <= eps = Nothing | otherwise = divForever lo hi -- | Perform split forever, until we cannot find a mid-value due to machine precision. -- This one uses `(/)` operator. divideForever :: (Eq a,Fractional a) => Splitter a divideForever lo hi = let mid = lo / 2 + hi / 2 in if lo == mid || mid == hi then Nothing else Just mid -- | Perform splitting until @hi - lo <= eps@. divideTill :: (Ord a, Fractional a) => a -> Splitter a divideTill eps lo hi | hi - lo <= eps = Nothing | otherwise = divideForever lo hi -- * Searching -- | Perform search over pure predicates. The monadic version of this is 'searchM'. search :: (Eq b) => SearchRange a -> Splitter a -> (a -> b) -> [Range b a] search init0 split0 pred0 = runIdentity $ searchM init0 split0 (Identity . pred0) -- | Mother of all search variations. -- -- 'searchM' keeps track of the predicates found, so that it works well with the 'Evidence' type. searchM :: forall a m b . (Functor m, Monad m, Eq b) => SearchRange a -> Splitter a -> (a -> m b) -> m [Range b a] searchM init0 split0 pred0 = do ranges0 <- initializeSearchM init0 pred0 go ranges0 where go :: [Range b a] -> m [Range b a] go (r1@(Range p0 lo1 p1 hi1):r2@(Range p2 lo2 p3 hi2):rest) = case split0 hi1 lo2 of Nothing -> (r1:) <$> go (r2:rest) Just mid1 -> do pMid <- pred0 mid1 if | p1 == pMid -> go $ (Range p0 lo1 pMid mid1) : r2 : rest | p2 == pMid -> go $ r1 : (Range pMid mid1 p3 hi2) : rest | otherwise -> go $ r1 : (Range pMid mid1 pMid mid1) : r2 : rest go xs = return xs -- * Postprocess -- | Look up for the first 'Range' with the given predicate. lookupRanges :: (Eq b) => b -> [Range b a] -> Maybe (Range b a) lookupRanges k [] = Nothing lookupRanges k (r@Range{..}:rs) | loKey == k = Just r | otherwise = lookupRanges k rs -- | Pick up the smallest @a@ that satisfies @pred a == b@. smallest :: (Eq b) => b -> [Range b a] -> Maybe a smallest b rs = loVal <$> lookupRanges b rs -- | Pick up the largest @a@ that satisfies @pred a == b@. largest :: (Eq b) => b -> [Range b a] -> Maybe a largest b rs = hiVal <$> lookupRanges b rs -- | Get the content of the evidence for the smallest @a@ which satisfies @pred a@. evidenceForSmallest :: [Range (Evidence b1 b2) a] -> Maybe b2 evidenceForSmallest rs = listToMaybe [e | Evidence e <- map loKey rs] -- | Get the content of the evidence for the largest @a@ which satisfies @pred a@. evidenceForLargest :: [Range (Evidence b1 b2) a] -> Maybe b2 evidenceForLargest rs = listToMaybe [e | Evidence e <- map hiKey rs] -- | Get the content of the counterEvidence for the smallest @a@ which does not satisfy @pred a@. counterEvidenceForSmallest :: [Range (Evidence b1 b2) a] -> Maybe b1 counterEvidenceForSmallest rs = listToMaybe [e | CounterEvidence e <- map loKey rs] -- | Get the content of the counterEvidence for the largest @a@ which does not satisfy @pred a@. counterEvidenceForLargest :: [Range (Evidence b1 b2) a] -> Maybe b1 counterEvidenceForLargest rs = listToMaybe [e | CounterEvidence e <- map hiKey rs] binary-search-2.0.0/Numeric/Search/0000755000000000000000000000000007346545000015240 5ustar0000000000000000binary-search-2.0.0/Numeric/Search/Bounded.hs0000644000000000000000000000501407346545000017154 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Numeric.Search.Bounded -- Copyright : (c) Ross Paterson 2008 -- License : BSD-style -- Maintainer : ross@soi.city.ac.uk -- Stability : experimental -- Portability : portable -- -- Searching unbounded intervals within bounded integral types for the -- boundary of an upward-closed set, using a combination of exponential -- and binary search. -- ----------------------------------------------------------------------------- -- module Numeric.Search.Bounded (search, searchFrom, searchTo) where import Numeric.Search.Range -- | /O(log(abs n))/. -- Search a bounded integral type. -- -- If @p@ is an upward-closed predicate, @search p@ returns -- @Just n@ if and only if @n@ is the least such satisfying @p@. search :: (Bounded a, Integral a) => (a -> Bool) -> Maybe a search p | p 0 = Just (searchDown p minBound 0) | otherwise = searchUp p 1 maxBound -- | /O(log(abs n))/. -- Search the part of a bounded integral type from a given point. -- -- If @p@ is an upward-closed predicate, @searchFrom p l@ returns -- @Just n@ if and only if @n@ is the least @n >= l@ satisfying @p@. searchFrom :: (Bounded a, Integral a) => (a -> Bool) -> a -> Maybe a searchFrom p l | l <= 0 && p 0 = Just (searchDown p l 0) | otherwise = searchUp p (max 1 l) maxBound -- | /O(log(abs n))/. -- Search the part of a bounded integral type up to a given point. -- -- If @p@ is an upward-closed predicate, @searchTo p h@ returns -- @Just n@ if and only if @n@ is the least @n <= h@ satisfying @p@. searchTo :: (Bounded a, Integral a) => (a -> Bool) -> a -> Maybe a searchTo p h | p h' = Just (searchDown p minBound h') | otherwise = searchUp p 1 h where h' = min 0 h -- @h <= 0 && p h@ searchDown :: (Integral a) => (a -> Bool) -> a -> a -> a searchDown p l h | l `quot` 2 >= h = searchSafeRange p l h | p h' = searchDown p l h' | otherwise = searchSafeRange p (h'+1) h where h' = h*2 - 1 -- @0 < l@ searchUp :: (Integral a) => (a -> Bool) -> a -> a -> Maybe a searchUp p l h | h `div` 2 <= l = searchFromTo p l h | p l' = Just (searchSafeRange p l l') | otherwise = searchUp p (l'+1) h where l' = l*2 + 1 -- | Like 'search', but assuming @l <= h && p h@. searchSafeRange :: Integral a => (a -> Bool) -> a -> a -> a searchSafeRange p l h | l == h = l | p m = searchSafeRange p l m | otherwise = searchSafeRange p (m+1) h -- Stay within @min 0 l .. max 0 h@ to avoid overflow. where m = l `div` 2 + h `div` 2 -- If l < h, then l <= m < h binary-search-2.0.0/Numeric/Search/Integer.hs0000644000000000000000000001060307346545000017171 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Numeric.Search.Integer -- Copyright : (c) Ross Paterson 2008 -- License : BSD-style -- Maintainer : ross@soi.city.ac.uk -- Stability : experimental -- Portability : portable -- -- Searching unbounded intervals of integers for the boundary of an -- upward-closed set, using a combination of exponential and binary -- search. -- ----------------------------------------------------------------------------- module Numeric.Search.Integer ( -- * One-dimensional searches search, searchFrom, searchTo, -- * Two-dimensional searches search2) where import Data.Maybe (fromMaybe) -- | /O(log(abs n))/. -- Search the integers. -- -- If @p@ is an upward-closed predicate, @search p@ returns the least -- @n@ satisfying @p@. If no such @n@ exists, either because no integer -- satisfies @p@ or all do, @search p@ does not terminate. -- -- For example, the following function computes discrete logarithms (base 2): -- -- > discreteLog :: Integer -> Integer -- > discreteLog n = search (\ k -> 2^k <= n) -- search :: (Integer -> Bool) -> Integer search p = fromMaybe (searchFrom p 1) (searchTo p 0) -- | /O(log(n-l))/. -- Search the integers from a given lower bound. -- -- If @p@ is an upward-closed predicate, -- @searchFrom p l = 'search' (\\ i -> i >= l && p i)@. -- If no such @n@ exists (because no integer satisfies @p@), -- @searchFrom p@ does not terminate. searchFrom :: (Integer -> Bool) -> Integer -> Integer searchFrom p = search_from 1 where search_from step l | p l' = searchIntegerRange p l (l'-1) | otherwise = search_from (2*step) (l'+1) where l' = l + step -- | /O(log(h-n))/. -- Search the integers up to a given upper bound. -- -- If @p@ is an upward-closed predicate, @searchTo p h == 'Just' n@ -- if and only if @n@ is the least number @n <= h@ satisfying @p@. searchTo :: (Integer -> Bool) -> Integer -> Maybe Integer searchTo p h0 | p h0 = Just (search_to 1 h0) | otherwise = Nothing where search_to step h -- @step >= 1 && p h@ | p h' = search_to (2*step) h' | otherwise = searchSafeRange p (h'+1) h where h' = h - step -- | /O(m log(n\/m))/. -- Two-dimensional search, using an algorithm due described in -- -- * Richard Bird, /Saddleback search: a lesson in algorithm design/, -- in /Mathematics of Program Construction/ 2006, -- Springer LNCS vol. 4014, pp82-89. -- -- If @p@ is closed upwards in each argument on non-negative integers, -- @search2 p@ returns the minimal non-negative pairs satisfying @p@, -- listed in order of increasing x-coordinate. -- -- /m/ and /n/ refer to the smaller and larger dimensions of the -- rectangle containing the boundary. -- -- For example, -- -- > search2 (\ x y -> x^2 + y^2 >= 25) == [(0,5),(3,4),(4,3),(5,0)] -- search2 :: (Integer -> Integer -> Bool) -> [(Integer,Integer)] search2 p = search2Rect p 0 0 hx hy [] where hx = searchFrom (\ x -> p x 0) 0 hy = searchFrom (\ y -> p 0 y) 0 search2Rect :: (Integer -> Integer -> Bool) -> Integer -> Integer -> Integer -> Integer -> [(Integer,Integer)] -> [(Integer,Integer)] search2Rect p lx ly hx hy | lx > hx || ly > hy = id | lx == hx && ly == hy = if p lx ly then ((lx, ly) :) else id | hx-lx > hy-ly = let mx = (lx+hx) `div` 2 my = searchIntegerRange (\ y -> p mx y) ly hy in search2Rect p lx my mx hy . search2Rect p (mx+1) ly hx (my-1) | otherwise = let mx = searchIntegerRange (\ x -> p x my) lx hx my = (ly+hy) `div` 2 in search2Rect p lx (my+1) (mx-1) hy . search2Rect p mx ly hx my -- | Search a bounded interval of integers. -- -- If @p@ is an upward-closed predicate, -- -- > searchIntegerRange p l h = 'search' (\ i -> i >= l && p i || i > h) -- -- Cost: /O(log(h-l))/ calls to @p@. searchIntegerRange :: (Integer -> Bool) -> Integer -> Integer -> Integer searchIntegerRange p l h | h < l = h+1 | p m = searchIntegerRange p l (m-1) | otherwise = searchIntegerRange p (m+1) h where m = (l+h) `div` 2 -- | Like 'search', but assuming @l <= h && p h@. searchSafeRange :: (Integer -> Bool) -> Integer -> Integer -> Integer searchSafeRange p l h | l == h = l | p m = searchSafeRange p l m | otherwise = searchSafeRange p (m+1) h where m = (l + h) `div` 2 -- If l < h, then l <= m < h binary-search-2.0.0/Numeric/Search/Range.hs0000644000000000000000000000320407346545000016627 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Numeric.Search.Range -- Copyright : (c) Ross Paterson 2008 -- License : BSD-style -- Maintainer : ross@soi.city.ac.uk -- Stability : experimental -- Portability : portable -- -- Binary search of a bounded interval of an integral type for the -- boundary of an upward-closed set, using a combination of exponential -- and binary search. -- ----------------------------------------------------------------------------- module Numeric.Search.Range (searchFromTo) where -- | /O(log(h-l))/. -- Search a bounded interval of some integral type. -- -- If @p@ is an upward-closed predicate, @searchFromTo p l h == Just n@ -- if and only if @n@ is the least number @l <= n <= h@ satisfying @p@. -- -- For example, the following function determines the first index (if any) -- at which a value occurs in an ordered array: -- -- > searchArray :: Ord a => a -> Array Int a -> Maybe Int -- > searchArray x array = do -- > let (lo, hi) = bounds array -- > k <- searchFromTo (\ i -> array!i >= x) lo hi -- > guard (array!k == x) -- > return k -- searchFromTo :: Integral a => (a -> Bool) -> a -> a -> Maybe a searchFromTo p l h | l > h = Nothing | p h = Just (searchSafeRange p l h) | otherwise = Nothing -- | Like 'searchFromTo', but assuming @l <= h && p h@. searchSafeRange :: Integral a => (a -> Bool) -> a -> a -> a searchSafeRange p l h | l == h = l | p m = searchSafeRange p l m | otherwise = searchSafeRange p (m+1) h -- Stay within @min 0 l .. max 0 h@ to avoid overflow. where m = l `div` 2 + h `div` 2 -- If l < h, then l <= m < h binary-search-2.0.0/Setup.hs0000644000000000000000000000005607346545000014066 0ustar0000000000000000import Distribution.Simple main = defaultMain binary-search-2.0.0/binary-search.cabal0000644000000000000000000000572207346545000016152 0ustar0000000000000000cabal-version: 2.0 Name: binary-search Version: 2.0.0 Build-Type: Simple License: BSD3 license-file: LICENSE Author: supercede , Ross Paterson , Takayuki Muranushi Maintainer: supercede Category: Algorithms Synopsis: Binary and exponential searches Description: __Introduction__ . This package provides varieties of binary search functions. c.f. "Numeric.Search" for the examples. . These search function can search for pure and monadic predicates, of type: . > pred :: Eq b => a -> b > pred :: (Eq b, Monad m) => a -> m b . The predicates must satisfy that the domain range for any codomain value is continuous; that is, @∀x≦y≦z. pred x == pred z ⇒ pred y == pred x@ . . For example, we can address the problem of finding the boundary of an upward-closed set of integers, using a combination of exponential and binary searches. . Variants are provided for searching within bounded and unbounded intervals of both 'Integer' and bounded integral types. . The package was created by Ross Paterson, and extended by Takayuki Muranushi, to be used together with SMT solvers. . __The Module Structure__ . * "Numeric.Search" provides the generic search combinator, to search for pure and monadic predicates. . * "Numeric.Search.Bounded" , "Numeric.Search.Integer" , "Numeric.Search.Range" provides the various specialized searchers, which means less number of function arguments, and easier to use. . <> library default-language: Haskell2010 exposed-modules: Numeric.Search Numeric.Search.Bounded Numeric.Search.Integer Numeric.Search.Range Ghc-Options: -Wall build-depends: base >=4.5 && < 5 , transformers , containers >= 0.4 Test-Suite doctest default-language: Haskell2010 Type: exitcode-stdio-1.0 HS-Source-Dirs: test Ghc-Options: -threaded -Wall Main-Is: doctests.hs Build-Depends: base , directory >= 1.1 , filepath >= 1.2 , doctest >= 0.9.3 Test-Suite spec default-language: Haskell2010 Type: exitcode-stdio-1.0 Hs-Source-Dirs: test Ghc-Options: -Wall Main-Is: Spec.hs Other-Modules: PureSpec Build-Depends: base >=4.7 && < 5 , binary-search , hspec >= 1.3 , QuickCheck >= 2.5 Source-Repository head Type: git Location: https://github.com/riskbook/binary-search binary-search-2.0.0/test/0000755000000000000000000000000007346545000013410 5ustar0000000000000000binary-search-2.0.0/test/PureSpec.hs0000644000000000000000000000356107346545000015477 0ustar0000000000000000module PureSpec (spec) where import Test.Hspec import Test.Hspec.QuickCheck (prop) import Numeric.Search.Bounded as B import Numeric.Search.Integer as I import Numeric.Search.Range spec :: Spec spec = do describe "Integer search" $ do prop "finds n when search for (>= n)" $ \n -> I.search (>= n) == (n :: Integer) prop "finds (max l n) when search for (>= n) in range (>= l)" $ \n l -> I.searchFrom (>= n) l == max l n prop "finds n when search for (>=n) in range (<=h), iff n <= h." $ \n h -> I.searchTo (>= n) h == if n <= h then Just (n :: Integer) else Nothing describe "Range search" $ do prop "returns Nothing for always failing predicate." $ \l h -> searchFromTo (const False) l (h :: Int) == Nothing prop "finds n given that n is within the range." $ \n l h -> searchFromTo (>= n) l h == let k = max n l in if k <= h then Just (k::Int) else Nothing describe "Bounded search" $ do prop "always finds n when searched for (>=n), by default." $ \n -> B.search (>= n) == Just (n :: Int) it "fails when given always failing predicate." $ B.search (const False :: Int -> Bool) `shouldBe` Nothing it "finds the lower bound when given an always-holding predicate." $ B.search (const True :: Int -> Bool) `shouldBe` Just minBound prop "finds (max l n) for lower-bounded search." $ \l n -> B.searchFrom (>= n) l == Just (max l (n::Int)) prop "finds Nothing for always-failing predicate with a bound." $ \l -> B.searchFrom (const False) (l::Int) == Nothing prop "finds n for upper-bounded search, iff n is within the bound." $ \n h -> B.searchTo (>= n) h == if n <= h then Just (n::Int) else Nothing prop "finds Nothing for always-failing predicate with a bound." $ \h -> B.searchTo (const False) (h::Int) == Nothing binary-search-2.0.0/test/Spec.hs0000644000000000000000000000005407346545000014635 0ustar0000000000000000{-# OPTIONS_GHC -F -pgmF hspec-discover #-} binary-search-2.0.0/test/doctests.hs0000644000000000000000000000113007346545000015567 0ustar0000000000000000module Main where import Control.Applicative import Control.Monad import Test.DocTest import System.Directory import System.FilePath findHs :: FilePath -> IO [FilePath] findHs dir = do fs <- map (dir ) <$> filter (`notElem` ["..","."]) <$> getDirectoryContents dir subDirs <- filterM doesDirectoryExist fs files1 <- filter ((`elem` [".hs", ".lhs"]) . takeExtension) <$> filterM doesFileExist fs files2 <- concat <$> mapM findHs subDirs return $ files1 ++ files2 main :: IO () main = do files <- findHs "Numeric" putStrLn $ "testing: " ++ unwords files doctest files