```{-# LANGUAGE FlexibleContexts #-}
{-|
Module      :  Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Derivative
Description :  (internal) derivative of polynomials
Copyright   :  (c) 2009 Michal Konecny

Maintainer  :  mik@konecny.aow.cz
Stability   :  experimental
Portability :  portable

Internal module for "Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom".

Implementation of safely rounded derivative of polynomials.
-}
module Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Derivative where

import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Basic
import Data.Number.ER.RnToRm.UnitDom.ChebyshevBase.Polynom.Ring

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

import qualified Data.Map as Map

{-|
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, ERChebPoly box b)
chplDifferentiate p diffVar = chplBall2DownUp \$ ballDifferentiate p diffVar

{-|
Differentiate a polynomial using one of its variables.
-}
ballDifferentiate ::
(B.ERRealBase b, DomainBox box varid Int, Ord box) =>
ERChebPoly box b ->
varid {-^ variable to differentiate over -} ->
(ERChebPoly box b, b)
ballDifferentiate (ERChebPoly coeffs) diffVar =
where
Map.foldWithKey extractTerm (Map.empty, 0) coeffs
extractTerm term c prevBall =
where
diffVarDegree = DBox.findWithDefault 0 diffVar term
cConstUp = c * (B.fromIntegerUp \$ toInteger diffVarDegree)
cConstDown = c `timesDown` (B.fromIntegerDown \$ toInteger diffVarDegree)
cConstErr = cConstUp - cConstDown
cNonconstUp = 2 * cConstUp
cNonconstDown = 2 `timesDown` cConstDown
cNonconstErr = cNonconstUp - cNonconstDown
| degreeToAdd < 0 = ball
| otherwise =
where
(newCoeffs, radius + cErr + newCoeffErr)
where
newCoeffs = Map.insert newTerm newCoeffDown coeffs
newCoeffUp = oldCoeff + c
newCoeffDown = oldCoeff `plusDown` c
newCoeffErr = newCoeffUp - newCoeffDown
oldCoeff =
case Map.lookup newTerm coeffs of
Nothing -> 0
Just c -> c
newTerm = DBox.insert diffVar degreeToAdd term