{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module MathObj.Polynomial
(T, fromCoeffs, coeffs, degree,
showsExpressionPrec, const,
evaluate, evaluateCoeffVector, evaluateArgVector,
collinear,
integrate,
compose, fromRoots, reverse,
translate, dilate, shrink, )
where
import qualified MathObj.Polynomial.Core as Core
import qualified Algebra.Differential as Differential
import qualified Algebra.VectorSpace as VectorSpace
import qualified Algebra.Module as Module
import qualified Algebra.Vector as Vector
import qualified Algebra.Field as Field
import qualified Algebra.PrincipalIdealDomain as PID
import qualified Algebra.Units as Units
import qualified Algebra.IntegralDomain as Integral
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import qualified Algebra.ZeroTestable as ZeroTestable
import qualified Algebra.Indexable as Indexable
import Control.Monad (liftM, )
import qualified Data.List as List
import Test.QuickCheck (Arbitrary(arbitrary))
import qualified MathObj.Wrapper.Haskell98 as W98
import NumericPrelude.Base hiding (const, reverse, )
import NumericPrelude.Numeric
import qualified Prelude as P98
newtype T a = Cons {coeffs :: [a]}
{-# INLINE fromCoeffs #-}
fromCoeffs :: [a] -> T a
fromCoeffs = lift0
{-# INLINE lift0 #-}
lift0 :: [a] -> T a
lift0 = Cons
{-# INLINE lift1 #-}
lift1 :: ([a] -> [a]) -> (T a -> T a)
lift1 f (Cons x0) = Cons (f x0)
{-# INLINE lift2 #-}
lift2 :: ([a] -> [a] -> [a]) -> (T a -> T a -> T a)
lift2 f (Cons x0) (Cons x1) = Cons (f x0 x1)
degree :: (ZeroTestable.C a) => T a -> Maybe Int
degree x =
case Core.normalize (coeffs x) of
[] -> Nothing
(_:xs) -> Just $ length xs
instance Functor T where
fmap f (Cons xs) = Cons (map f xs)
{-# INLINE plusPrec #-}
{-# INLINE appPrec #-}
plusPrec, appPrec :: Int
plusPrec = 6
appPrec = 10
instance (Show a) => Show (T a) where
showsPrec p (Cons xs) =
showParen (p >= appPrec) (showString "Polynomial.fromCoeffs " . shows xs)
{-# INLINE showsExpressionPrec #-}
showsExpressionPrec :: (Show a, ZeroTestable.C a, Additive.C a) =>
Int -> String -> T a -> String -> String
showsExpressionPrec p var poly =
if isZero poly
then showString "0"
else
let terms = filter (not . isZero . fst)
(zip (coeffs poly) monomials)
monomials = id :
showString "*" . showString var :
map (\k -> showString "*" . showString var
. showString "^" . shows k)
[(2::Int)..]
showsTerm x showsMon = showsPrec (plusPrec+1) x . showsMon
in showParen (p > plusPrec)
(foldl (.) id $ List.intersperse (showString " + ") $
map (uncurry showsTerm) terms)
{-# INLINE evaluate #-}
evaluate :: Ring.C a => T a -> a -> a
evaluate (Cons y) x = Core.horner x y
{-# INLINE evaluateCoeffVector #-}
evaluateCoeffVector :: Module.C a v => T v -> a -> v
evaluateCoeffVector (Cons y) x = Core.hornerCoeffVector x y
{-# INLINE evaluateArgVector #-}
evaluateArgVector :: (Module.C a v, Ring.C v) => T a -> v -> v
evaluateArgVector (Cons y) x = Core.hornerArgVector x y
{-# INLINE compose #-}
compose :: (Ring.C a) => T a -> T a -> T a
compose (Cons x) y = Core.horner y (map const x)
{-# INLINE const #-}
const :: a -> T a
const x = lift0 [x]
collinear :: (Eq a, Ring.C a) => T a -> T a -> Bool
collinear (Cons x) (Cons y) = Core.collinear x y
instance (Eq a, ZeroTestable.C a) => Eq (T a) where
(Cons x) == (Cons y) = Core.equal x y
instance (Indexable.C a, ZeroTestable.C a) => Indexable.C (T a) where
compare = Indexable.liftCompare coeffs
instance (ZeroTestable.C a) => ZeroTestable.C (T a) where
isZero (Cons x) = isZero x
instance (Additive.C a) => Additive.C (T a) where
(+) = lift2 Core.add
(-) = lift2 Core.sub
zero = lift0 []
negate = lift1 Core.negate
instance Vector.C T where
zero = zero
(<+>) = (+)
(*>) = Vector.functorScale
instance (Module.C a b) => Module.C a (T b) where
(*>) x = lift1 (x *>)
instance (Field.C a, Module.C a b) => VectorSpace.C a (T b)
instance (Ring.C a) => Ring.C (T a) where
one = const one
fromInteger = const . fromInteger
(*) = lift2 Core.mul
instance (ZeroTestable.C a, Field.C a) => Integral.C (T a) where
divMod (Cons x) (Cons y) =
let (d,m) = Core.divMod x y
in (Cons d, Cons m)
instance (ZeroTestable.C a, Field.C a) => Units.C (T a) where
isUnit (Cons []) = False
isUnit (Cons (x0:xs)) = not (isZero x0) && all isZero xs
stdUnit (Cons x) = const (Core.stdUnit x)
stdUnitInv (Cons x) = const (recip (Core.stdUnit x))
instance (ZeroTestable.C a, Field.C a) => PID.C (T a)
instance (Ring.C a) => Differential.C (T a) where
differentiate = lift1 Core.differentiate
{-# INLINE integrate #-}
integrate :: (Field.C a) => a -> T a -> T a
integrate = lift1 . Core.integrate
{-# INLINE fromRoots #-}
fromRoots :: (Ring.C a) => [a] -> T a
fromRoots = Cons . foldl (flip Core.mulLinearFactor) [one]
{-# INLINE reverse #-}
reverse :: Additive.C a => T a -> T a
reverse = lift1 Core.alternate
translate :: Ring.C a => a -> T a -> T a
translate d =
lift1 $ foldr (\c p -> [c] + Core.mulLinearFactor d p) []
shrink :: Ring.C a => a -> T a -> T a
shrink = lift1 . Core.shrink
dilate :: Field.C a => a -> T a -> T a
dilate = lift1 . Core.dilate
instance (Arbitrary a, ZeroTestable.C a) => Arbitrary (T a) where
arbitrary = liftM (fromCoeffs . Core.normalize) arbitrary
{-# INLINE notImplemented #-}
notImplemented :: String -> a
notImplemented name =
error $ "MathObj.Polynomial: method " ++ name ++ " cannot be implemented"
instance (P98.Num a) => P98.Num (T a) where
fromInteger = const . P98.fromInteger
negate = W98.unliftF1 Additive.negate
(+) = W98.unliftF2 (Additive.+)
(*) = W98.unliftF2 (Ring.*)
abs = notImplemented "abs"
signum = notImplemented "signum"
instance (P98.Fractional a) => P98.Fractional (T a) where
fromRational = const . P98.fromRational
(/) = notImplemented "(/)"