{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module Numeric.Ring.Endomorphism 
  ( End(..)
  , toEnd
  , fromEnd
  , frobenius
  ) where

import Data.Monoid
import Numeric.Algebra
import Prelude hiding ((*),(+),(-),negate,subtract)
import Data.Proxy

-- | The endomorphism ring of an abelian group or the endomorphism semiring of an abelian monoid
-- 
-- http://en.wikipedia.org/wiki/Endomorphism_ring
newtype End a = End { appEnd :: a -> a }
instance Monoid (End r) where
  mappend (End a) (End b) = End (a . b)
  mempty = End id
instance Additive r => Additive (End r) where
  End f + End g = End (f + g)
instance Abelian r => Abelian (End r)
instance Monoidal r => Monoidal (End r) where
  zero = End (const zero)
instance Group r => Group (End r) where
  End f - End g = End (f - g)
  negate (End f) = End (negate f)
  subtract (End f) (End g) = End (subtract f g)
instance Multiplicative (End r) where
  End f * End g = End (f . g)
instance Unital (End r) where
  one = End id
instance (Abelian r, Commutative r) => Commutative (End r) 
instance (Abelian r, Monoidal r) => Semiring (End r)
instance (Abelian r, Monoidal r) => Rig (End r)
instance (Abelian r, Group r) => Ring (End r)
instance (Monoidal m, Abelian m) => LeftModule (End m) (End m) where
  End f .* End g = End (f . g)
instance (Monoidal m, Abelian m) => RightModule (End m) (End m) where
  End f *. End g = End (f . g)
instance LeftModule r m => LeftModule r (End m) where
  r .* End f = End (\e -> r .* f e)
instance RightModule r m => RightModule r (End m) where
  End f *. r = End (\e -> f e *. r)

-- TODO: Involutive? Invertible?
-- instance SimpleAdditiveAbelianGroup r => DivisionRing (End r) where

-- ring isomorphism from r to the endomorphism ring of r.
toEnd :: Multiplicative r => r -> End r
toEnd r = End (*r)

-- ring isomorphism from the endormorphism ring of r to r.
fromEnd :: Unital r => End r -> r
fromEnd (End f) = f one

-- the frobenius ring endomorphism (assuming the characteristic is prime)
frobenius :: Characteristic r => End r
frobenius = End $ \r -> r `pow` char (ofRing r)

ofRing :: r -> Proxy r
ofRing _ = Proxy