-- | Univariate \"monomials\" (basically the natural numbers)

{-# LANGUAGE BangPatterns, DataKinds, KindSignatures, TypeFamilies #-}
module Math.Algebra.Polynomial.Monomial.Univariate where

--------------------------------------------------------------------------------

import Data.Array ( assocs )
import Data.List

#if MIN_VERSION_base(4,11,0)        
import Data.Semigroup
import Data.Monoid
#else
import Data.Monoid
#endif

import Data.Typeable
import GHC.TypeLits
import Data.Proxy

import Math.Algebra.Polynomial.Class
import Math.Algebra.Polynomial.Pretty
import Math.Algebra.Polynomial.Misc

--------------------------------------------------------------------------------
-- * Univariate monomials

-- | A monomial in a univariate polynomial, indexed by its name, eg @U "x"@
newtype U (var :: Symbol) = U Int deriving (Eq,Ord,Show,Typeable)

-- | Name of the variable
uVar :: KnownSymbol var => U var -> String
uVar = symbolVal . uproxy where
  uproxy :: U var -> Proxy var
  uproxy _ = Proxy

instance KnownSymbol var => Pretty (U var) where
  pretty u@(U e) = case e of
    0 -> "1"
    1 -> uVar u
    _ -> uVar u ++ "^" ++ show e

--------------------------------------------------------------------------------

#if MIN_VERSION_base(4,11,0)        

instance Semigroup (U var) where
  (<>) (U e) (U f) = U (e+f)

instance Monoid (U var) where
  mempty = U 0
  mappend (U e) (U f) = U (e+f)
  mconcat us = U $ sum' [ e | U e <- us ]

#else

instance Monoid (U var) where
  mempty  = U 0
  mappend (U e) (U f) = U (e+f)
  mconcat us = U $ sum' [ e | U e <- us ]

#endif

--------------------------------------------------------------------------------

instance KnownSymbol var => Monomial (U var) where
  -- | the type of variables
  type VarM (U var) = ()

  -- checking the invariant
  normalizeM  = id
  isNormalM   = const True

  -- construction and deconstruction
  fromListM   ves = U $ sum' (map snd ves)
  toListM     (U e) = [((),e)]

  -- simple monomials
  emptyM      = U 0
  isEmptyM    (U e) = (e==0)
  variableM   _   = U 1
  singletonM  _ e = U e

  -- algebra
  mulM         = mappend
  productM     = mconcat
  divM (U e) (U f) = if e >= f then Just (U (e-f)) else Nothing
  powM (U e) k = U (k*e)

  -- degrees
  maxDegM     (U e) = e
  totalDegM   (U e) = e

  -- calculus
  diffM _ = diffU

  -- substitution and evaluation
  evalM       f (U e) = (f ())^e
  varSubsM    _ = id
  termSubsM   f (U e, c) = case f () of
                Nothing  -> (U e, c      )
                (Just x) -> (U 0, c * x^e)

--------------------------------------------------------------------------------
-- * differentiation

diffU :: Num c => Int -> U v -> Maybe (U v, c)
diffU k (U m) =
  if k > m
    then Nothing
    else Just (U (m-k) , fromInteger c)
  where
    c = product [ fromIntegral (m-i) | i<-[0..k-1] ] :: Integer

--------------------------------------------------------------------------------