{- - ``Data/Random/Internal/Find'' - Utilities for searching fractional domains. Needs cleanup, testing, - and such. Used for constructing generic ziggurats. -} module Data.Random.Internal.Find where findMax :: (Fractional a, Ord a) => (a -> Bool) -> a findMax p = negate (findMin (p.negate)) -- |Given an upward-closed predicate on an ordered Fractional type, -- find the smallest value satisfying the predicate. findMin :: (Fractional a, Ord a) => (a -> Bool) -> a findMin = findMinFrom 0 1 -- |Given an upward-closed predicate on an ordered Fractional type, -- find the smallest value satisfying the predicate. Starts at the -- specified point with the specified stepsize, performs an exponential -- search out from there until it finds an interval bracketing the -- change-point of the predicate, and then performs a bisection search -- to isolate the change point. Note that infinitely-divisible domains -- such as 'Rational' cannot be searched by this function because it does -- not terminate until it reaches a point where further subdivision of the -- interval has no effect. findMinFrom :: (Fractional a, Ord a) => a -> a -> (a -> Bool) -> a findMinFrom z0 0 p = findMinFrom z0 1 p findMinFrom z0 step1 p | p z0 = descend (z0-step1) z0 | otherwise = fixZero (ascend z0 (z0+step1)) where -- eliminate negative zero, which, in many domains, is technically -- a feasible answer fixZero 0 = 0 fixZero z = z -- preconditions: -- not (p l) -- 0 <= l < x ascend l x | p x = bisect l x | otherwise = ascend x $! 2*x-z0 -- preconditions: -- p h -- x < h <= 0 descend x h | p x = (descend $! 2*x-z0) x | otherwise = bisect x h -- preconditions: -- not (p l) -- p h -- l <= h bisect l h | l /< h = h | l /< mid || mid /< h = if p mid then mid else h | p mid = bisect l mid | otherwise = bisect mid h where a /< b = not (a < b) mid = (l+h)*0.5