module Polynomial (
   T, fromScalar, add, sub, neg, scale, mul,
   differentiate, progression,
   ) where


type T a = [a]


fromScalar :: a -> [a]
fromScalar = (:[])

-- | add two polynomials or series
add :: Num a => [a] -> [a] -> [a]
{- zipWith (+) would cut the resulting list
   to the length of the shorter operand -}
add [] ys = ys
add xs [] = xs
add (x:xs) (y:ys) = x+y : add xs ys

-- | subtract two polynomials or series
sub :: Num a => [a] -> [a] -> [a]
sub [] ys = map negate ys
sub xs [] = xs
sub (x:xs) (y:ys) = x-y : sub xs ys

neg :: Num a => [a] -> [a]
neg = map negate

-- | scale a polynomial or series by a factor
scale :: Num a => a -> [a] -> [a]
scale s = map (s*)


-- | multiply two polynomials or series
mul :: Num a => [a] -> [a] -> [a]
{- prevent from generation of many zeros
   if the first operand is the empty list -}
mul [] = const []
mul xs = foldr (\y zs -> add (scale y xs) (0:zs)) []


progression :: Num a => [a]
progression = iterate (1+) 1


differentiate :: (Num a) => [a] -> [a]
differentiate x = zipWith (*) (tail x) progression