-- | The 'Linear' module provides the tools for treating a given 'Manifold' as a
-- linear space.
module Goal.Geometry.Linear (
    -- * Vector Spaces
      (<+>)
    , (.>)
    , (<->)
    , (/>)
    , meanPoint
    -- * Dual Spaces
    , Primal
    , Dual
    , (<.>)
    ) where

--- Imports ---

import Prelude hiding (map,minimum,maximum)

-- Package --

import Goal.Core hiding (dot)
import Goal.Geometry.Manifold

-- Unqualified --

import Numeric.LinearAlgebra.HMatrix hiding (Field,(><),(<>),(<.>))

--import Data.Vector.Storable.UnsafeSerialize


--- Vector Spaces on Manifolds ---


infixl 6 <+>
(<+>) :: Manifold m => c :#: m -> c :#: m -> c :#: m
-- | Vector addition of points on a manifold.
(<+>) p p' = fromCoordinates (manifold p) (coordinates p' + coordinates p)
{-
  | m == manifold p' = fromCoordinates m (coordinates p' + coordinates p)
  | otherwise = error "Attempting to add points from distinct manifolds."
    where m = manifold p
          -}

infixl 6 <->
(<->) :: Manifold m => c :#: m -> c :#: m -> c :#: m
-- | Vector subtraction of points on a manifold.
(<->) p p' = fromCoordinates (manifold p) (coordinates p - coordinates p')
{-
  | m == manifold p' = fromCoordinates m (coordinates p - coordinates p')
  | otherwise = error "Attempting to subtract points from distinct manifolds."
    where m = manifold p
          -}


infix 7 .>
(.>) :: Manifold m => Double -> c :#: m -> c :#: m
-- | Scalar multiplication of points on a manifold.
(.>) a = alterCoordinates (*a)

infix 7 />
(/>) :: Manifold m => Double -> c :#: m -> c :#: m
-- | Scalar division of points on a manifold.
(/>) a v = recip a .> v


--- Dual Spaces ---


-- | 'Primal' charts have a 'Dual' coordinate system. The 'Dual' coordinate
-- system is the system which determines the dual basis of the dual vector
-- space via the restriction that the inner product '<.>' be the dot product.
--
-- Since finite dimensional vector spaces are isomorphic to their dual spaces
-- through the dual basis,  vector space duality is handled purely at the level
-- of coordinates in Goal -- that is, 'Primal' and 'Dual' coordinates are
-- considered different ways of describing the same fundamental objects. In
-- practice, encoding this relationship purely at the level of Charts saves a
-- great deal of computational effort.
class (Dual (Dual c)) ~ c => Primal c where
    type Dual c :: *

infix 7 <.>
(<.>) :: c :#: m -> Dual c :#: m -> Double
-- | '<.>' is the inner product between a dual pair of 'Point's. The defining
-- property of 'Dual' coordinate systems is that the inner product can be
-- expressed as a dot product.
(<.>) p q = dot (coordinates p) (coordinates q)

-- Utility --

meanPoint :: Manifold m => [c :#: m] -> c :#: m
-- | Finds the midpoint amongst a set of vectors in a convex set.
meanPoint ps = fromCoordinates (manifold $ head ps) . mean $ coordinates <$> ps
  {-
  | all (== m) (manifold <$> ps) = fromCoordinates m . mean $ coordinates <$> ps
  | otherwise = error "Attempting to add points from distinct manifolds."
    where m = manifold $ head ps
          -}


--- Instances ---


-- Cartesian Spaces --

instance Primal Cartesian where
    type Dual Cartesian = Cartesian