{-# LANGUAGE FlexibleContexts #-}
{-|
    Module      :  Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Integration
    Description :  (internal) integration of polynomials etc  
    Copyright   :  (c) 2007-2008 Michal Konecny
    License     :  BSD3

    Maintainer  :  mik@konecny.aow.cz
    Stability   :  experimental
    Portability :  portable
    
    Internal module for "Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom".
    
    Implementation of safely rounded integration of polynomials
    and other related functions.
-}
module Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Integration 
where

import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Basic
import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Eval
import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Field
import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Bounds

import qualified Data.Number.ER.Real.Base as B
import qualified Data.Number.ER.Real.DomainBox as DBox
import Data.Number.ER.Real.DomainBox (VariableID(..), DomainBox, DomainBoxMappable, DomainIntBox)
import Data.Number.ER.Misc

import qualified Data.Map as Map

{-|
     Approximate from below and from above the integral of a polynomial.
     
     Based on the following formulas for Chebyshev polynomials:
     
>     \int T_n(x)dx = 
>        T_{n+1}(x)/2(n+1) - T_{n-1}(x)/2(n-1)

>     \int T_1(x)dx = 
>        T_2(x)/4 + 1/4

>     \int T_0(x)dx = 
>        T_1(x)
 
-}
chplIntegrate ::
    (B.ERRealBase b, DomainBox box varid Int, Ord box) => 
    varid {-^ variable to integrate by -} -> 
    ERChebPoly box b ->
    (ERChebPoly box b, ERChebPoly box b)
chplIntegrate x (ERChebPoly coeffs) =
--    unsafePrint
--    (
--        "ERChebPoly: integrate:"
--        ++ "\n pNp1Down = " ++ chplShow True pNp1Down 
--        ++ "\n pNm1Up = " ++ chplShow True pNm1Up 
--    )
    (chplNormaliseDown $ pNp1Down - pNm1Up, 
     chplNormaliseUp $ pNp1Up - pNm1Down)
    where
    pNp1Up =
        ERChebPoly $ 
            Map.insertWith plusUp chplConstTermKey errBoundNp1 $ 
                Map.fromList coeffsNp1
    pNp1Down =
        ERChebPoly $ 
            Map.insertWith plusDown chplConstTermKey (- errBoundNp1) $ 
                Map.fromList coeffsNp1
    pNm1Up =
        ERChebPoly $ 
            Map.insertWith plusUp chplConstTermKey errBoundNm1 $ 
                Map.fromList coeffsNm1
    pNm1Down =
        ERChebPoly $ 
            Map.insertWith plusDown chplConstTermKey (- errBoundNm1) $ 
                Map.fromList coeffsNm1
    (coeffsNp1, errBoundNp1) =
        foldl cfNp1 ([],0) coeffsList
    (coeffsNm1, errBoundNm1) =
        foldl cfNm1 ([],0) coeffsList
    coeffsList = Map.toList coeffs
    cfNp1 (prevTerms, prevErr) (termKey, coeff)
        | n == 0 =
            ((termKeyNp1, coeff):prevTerms, prevErr)
        | n == 1 =
            ((termKeyNm1, coeff0Up):(termKeyNp1, coeffNp1Up):prevTerms, prevErr + coeff0Err + coeffNp1Err)
        | otherwise =
            ((termKeyNp1, coeffNp1Up):prevTerms, prevErr + coeffNp1Err)
        where
        termKeyNp1 = DBox.insert x (n + 1) termKey
        termKeyNm1 = DBox.insert x (n - 1) termKey 
        n = DBox.findWithDefault 0 x termKey
        coeffNp1Err = coeffNp1Up - coeffNp1Down 
        coeffNp1Up = coeff / (2*nB + 2)
        coeffNp1Down = -((-coeff) / (2*nB + 2))
        nB = fromInteger $ toInteger n
        coeff0Up = coeff / 4
        coeff0Down = - ((- coeff) / 4)
        coeff0Err = coeff0Up - coeff0Down 
    cfNm1 (prevTerms, prevErr) (termKey, coeff)
        | n == 0 || n == 1 =
            ((chplConstTermKey, 0):prevTerms, prevErr)
        | otherwise =
            ((termKeyNm1, coeffNm1Up):prevTerms, prevErr + coeffNm1Err)
        where
        termKeyNm1 = DBox.insert x (n - 1) termKey 
        n = DBox.findWithDefault 0 x termKey
        coeffNm1Up = coeff / (2*nB - 2)
        coeffNm1Down = -((-coeff) / (2*nB - 2))
        nB = fromInteger $ toInteger n
        coeffNm1Err = coeffNm1Up - coeffNm1Down 

{-|
    measure the volume between a polynomial and the zero axis on [-1,1]^n
-}
chplVolumeAboveZero ::
    (B.ERRealBase b, DomainBox box varid Int, Ord box, 
     DomainBoxMappable boxb boxbb varid b [(b,b)]) =>
    [varid] ->
    ERChebPoly box b ->
    (b,b)
chplVolumeAboveZero vars p@(ERChebPoly coeffs) =
--    unsafePrint ("chplVolumeAboveZero: returning:" ++ show result) $
--    unsafePrint ("chplVolumeAboveZero: vars = " ++ show vars) $
    result
    where
    result = 
        (- (integUpAtOddCorners - integDownAtEvenCorners), integUpAtEvenCorners - integDownAtOddCorners)
    integUpAtEvenCorners = sumUp $ map (\pt -> chplEvalUp pt integUp) evenCorners
    integUpAtOddCorners = sumUp $ map (\pt -> chplEvalUp pt integUp) oddCorners 
    integDownAtEvenCorners = sumDown $ map (\pt -> chplEvalDown pt integDown) evenCorners  
    integDownAtOddCorners = sumDown $ map (\pt -> chplEvalDown pt integDown) oddCorners
    evenCorners = map (DBox.fromList) evenCornersL
    oddCorners = map (DBox.fromList) oddCornersL
    (evenCornersL, oddCornersL) =
        allPairsCombinationsEvenOdd $ zip vars $ repeat (1,-1)
    integUp = integrateByAllVars snd p vars
    integDown = integrateByAllVars fst p vars
    integrateByAllVars pick p [] = p
    integrateByAllVars pick p (x : xs) =
        integrateByAllVars pick ip xs
        where
        ip = pick $ chplIntegrate x p
--    vars = chplGetVars p
      
    
--{-|
--    Calculate approximations to the Chebyshev nodes.
---}
--chebNodes ::
--    (B.ERRealBase b) =>
--    Granularity ->
--    [[b]] -- ^ ith element is the ordered list of ith order Chebyshev nodes  
--chebNodes gran =
--    error "ERChebPoly: chebNodes: not implemented yet"
    
    
{-|
    Differentiate a polynomial using one of its variables. 
-}
chplDifferentiate ::
    (B.ERRealBase b, DomainBox box varid Int, Ord box) => 
    ERChebPoly box b ->
    varid {-^ variable to differentiate over -} ->
    ERChebPoly box b
chplDifferentiate (ERChebPoly coeffs) varName =
    errorModule "chplDifferentiate: not implemented yet"