content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  -----------------------------------------------------------------------------
  -- |
  -- License     :  BSD-style (see the file LICENSE)
  -- Maintainer  :  Edward Kmett <ekmett@gmail.com>
  -- Stability   :  provisional
  -- Portability :  portable
  --
  -----------------------------------------------------------------------------
  module Linear.Algebra
    ( Algebra(..)
    , Coalgebra(..)
    , multRep, unitalRep
    , comultRep, counitalRep
    ) where
  
  import Control.Lens hiding (index)
  import Data.Functor.Rep
  import Data.Complex
  import Data.Void
  import Linear.Vector
  import Linear.Quaternion
  import Linear.Conjugate
  import Linear.V0
  import Linear.V1
  import Linear.V2
  import Linear.V3
  import Linear.V4
  
  -- | An associative unital algebra over a ring
  class Num r => Algebra r m where
    mult :: (m -> m -> r) -> m -> r
    unital :: r -> m -> r
  
  multRep :: (Representable f, Algebra r (Rep f)) => f (f r) -> f r
  multRep ffr = tabulate $ mult (index . index ffr)
  
  unitalRep :: (Representable f, Algebra r (Rep f)) => r -> f r
  unitalRep = tabulate . unital
  
  instance Num r => Algebra r Void where
    mult _ _ = 0
    unital _ _ = 0
  
  instance Num r => Algebra r (E V0) where
    mult _ _ = 0
    unital _ _ = 0
  
  instance Num r => Algebra r (E V1) where
    mult f _ = f ex ex
    unital r _ = r
  
  instance Num r => Algebra r () where
    mult f () = f () ()
    unital r () = r
  
  instance (Algebra r a, Algebra r b) => Algebra r (a, b) where
    mult f (a,b) = mult (\a1 a2 -> mult (\b1 b2 -> f (a1,b1) (a2,b2)) b) a
    unital r (a,b) = unital r a * unital r b
  
  instance Num r => Algebra r (E Complex) where
    mult f = \ i -> c^.el i where
     c = (f ee ee - f ei ei) :+ (f ee ei + f ei ee)
    unital r i = (r :+ 0)^.el i
  
  instance (Num r, TrivialConjugate r) => Algebra r (E Quaternion) where
    mult f = index $ Quaternion
      (f ee ee - (f ei ei + f ej ej + f ek ek))
      (V3 (f ee ei + f ei ee + f ej ek - f ek ej)
          (f ee ej + f ej ee + f ek ei - f ei ek)
          (f ee ek + f ek ee + f ei ej - f ej ei))
    unital r = index (Quaternion r 0)
  
  -- | A coassociative counital coalgebra over a ring
  class Num r => Coalgebra r m where
    comult :: (m -> r) -> m -> m -> r
    counital :: (m -> r) -> r
  
  comultRep :: (Representable f, Coalgebra r (Rep f)) => f r -> f (f r)
  comultRep fr = tabulate $ \i -> tabulate $ \j -> comult (index fr) i j
  
  counitalRep :: (Representable f, Coalgebra r (Rep f)) => f r -> r
  counitalRep = counital . index
  
  instance Num r => Coalgebra r Void where
    comult _ _ _ = 0
    counital _ = 0
  
  instance Num r => Coalgebra r () where
    comult f () () = f ()
    counital f = f ()
  
  instance Num r => Coalgebra r (E V0) where
    comult _ _ _ = 0
    counital _ = 0
  
  instance Num r => Coalgebra r (E V1) where
    comult f _ _ = f ex
    counital f = f ex
  
  instance Num r => Coalgebra r (E V2) where
    comult f = index . index v where
      v = V2 (V2 (f ex) 0) (V2 0 (f ey))
    counital f = f ex + f ey
  
  instance Num r => Coalgebra r (E V3) where
    comult f = index . index q where
      q = V3 (V3 (f ex) 0 0)
             (V3 0 (f ey) 0)
             (V3 0 0 (f ez))
    counital f = f ex + f ey + f ez
  
  instance Num r => Coalgebra r (E V4) where
    comult f = index . index v where
      v = V4 (V4 (f ex) 0 0 0) (V4 0 (f ey) 0 0) (V4 0 0 (f ez) 0) (V4 0 0 0 (f ew))
    counital f = f ex + f ey + f ez + f ew
  
  instance Num r => Coalgebra r (E Complex) where
    comult f = \i j -> c^.el i.el j where
      c = (f ee :+ 0) :+ (0 :+ f ei)
    counital f = f ee + f ei
  
  instance (Num r, TrivialConjugate r) => Coalgebra r (E Quaternion) where
    comult f = index . index
      (Quaternion (Quaternion (f ee) (V3 0 0 0))
              (V3 (Quaternion 0 (V3 (f ei) 0 0))
                  (Quaternion 0 (V3 0 (f ej) 0))
                  (Quaternion 0 (V3 0 0 (f ek)))))
    counital f = f ee + f ei + f ej + f ek
  
  instance (Coalgebra r m, Coalgebra r n) => Coalgebra r (m, n) where
    comult f (a1, b1) (a2, b2) = comult (\a -> comult (\b -> f (a, b)) b1 b2) a1 a2
    counital k = counital $ \a -> counital $ \b -> k (a,b)
  