    {-# 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