{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} module Math.Root.Finder.Secant ( SecantMethod, secant ) where import Math.Root.Finder -- | @secant f x1 x2 xacc@: Using the secant method, return the root of a -- function thought to lie between x1 and x2. The root is refined until its -- accuracy is +-xacc. secant :: (Ord a, Fractional a) => (a -> a) -> a -> a -> a -> Either (SecantMethod a a) a secant f x1 x2 xacc = fmap estimateRoot (findRoot f x1 x2 xacc) -- |Iteratively refine 2 estimates x1, x2 of a root of f until total -- convergence (which may or may not ever be achieved) using the -- secant method. data SecantMethod a b = ConvergedSecantMethod !a | SecantMethod { secDX :: !a , secXL :: !a , _secFL :: !b , _secRTS :: !a , _secFRTS :: !b } deriving (Eq, Show) instance (Fractional a, Ord a) => RootFinder SecantMethod a a where initRootFinder f x1 x2 | abs f1 < abs f2 = stepRootFinder f $ SecantMethod 0 x2 f2 x1 f1 | otherwise = stepRootFinder f $ SecantMethod 0 x1 f1 x2 f2 where f1 = f x1; f2 = f x2 stepRootFinder _ orig@ConvergedSecantMethod{} = orig stepRootFinder f (SecantMethod _ xl fl rts fRts) | fNew == 0 = ConvergedSecantMethod xNew | otherwise = SecantMethod dx rts fRts xNew fNew where dx = (xl - rts) * fRts / (fRts - fl) xNew = rts + dx fNew = f xNew estimateRoot (ConvergedSecantMethod x) = x estimateRoot SecantMethod{secXL = x} = x estimateError ConvergedSecantMethod{} = 0 estimateError SecantMethod{secDX = dx} = dx