{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module Math.Root.Finder.Newton
    ( Newton, newton
    ) where

import Math.Root.Finder

data Newton a b = Newton
    { newtRTN   :: !a
    , newtDX    :: a
    } deriving (Eq, Show)

instance Fractional a => RootFinder Newton a (a,a) where
    initRootFinder f x1 x2 = stepRootFinder f (Newton rtn undefined)
        where
            rtn = 0.5 * (x1 + x2)
    
    stepRootFinder f Newton{newtRTN = rtn} = Newton (rtn - dx) dx
        where
            (y,dy) = f rtn
            dx = y / dy
    
    estimateRoot Newton{newtRTN = rtn} = rtn
    estimateError Newton{newtDX = dx}  = dx    

-- | @newton f x1 x2 xacc@:  using Newton's method, return a root of a
-- function known to lie between x1 and x2.  The root is refined until its
-- accuracy is += xacc.
-- 
-- The function passed should return a pair containing the value of the
-- function and its derivative, respectively.
newton :: (Ord a, Fractional a) => (a -> (a, a)) -> a -> a -> a -> Either (Newton a (a,a)) a
newton f x1 x2 xacc = fmap estimateRoot (findRoot f x1 x2 xacc)