{-# LANGUAGE FlexibleContexts #-}
-- | Helpers for implicit integration methods
--
-- TODO: add possibility to make function to create initial value
-- TODO: add possibility to break on step
-- TODO: add possibility to add different initial value based
--          on y0, f
-- TODO: add seq-pseq to make this stuff strict
-- TODO: add Newton iterations
module Math.Integrators.Implicit
    ( -- * types
      ImplicitSolver
      -- * solvers
    , fixedPointSolver
    , fixedPoint
      -- * helpers
    , breakNormR
    , breakNormIR
    )
    where

import Linear
import Control.Lens

-- | Implicit solver type
type ImplicitSolver a = (a -> a)                    -- ^ implicit method
                        -> (Int -> a -> a -> Bool)  -- ^ breakRule
                        -> a                        -- ^ initial value
                        -> a                        -- ^ final value

-- | Fixed point method it iterates function f until it will break "" will
-- be reached then it returns one but last iteration
--
fixedPointSolver :: ImplicitSolver a
fixedPointSolver f break' y0 = inner 0 y0
    where 
        inner i y = let y' = f y
                        i' = i+1
                    in if break' i y y'
                           then y'
                           else inner i' y'

fixedPoint :: (a -> a)            -- ^ function
              -> (a -> a -> Bool) -- ^ break rule
              -> a                -- ^ initial value
              -> a                -- ^ result
fixedPoint f break' y0 = 
    let y1 = f y0
    in if break' y0 y1
        then y0
        else fixedPoint f break' y1

-- | simple break rule that will break evaluatioin when value less then Eps
breakNormR :: Double -> Double -> Bool
breakNormR eps y =  abs y < eps

-- | same as @breakNormR@ but assume that inner type is an 
-- instance of InnerField, so it's possible to use innerproduct to find norm
-- N.B function uses $||v||^2 < eps$, so epsilon should be pre evaluated
breakNormIR :: (Metric f, Floating a, Ord a, Num (f a)) => f a -> a -> Bool
breakNormIR v eps = quadrance v < eps