module Data.AffineSpace
  (
    AffineSpace(..), (.-^), distanceSq, distance, alerp
  ) where
import Control.Applicative (liftA2)
import Data.Ratio
import Data.VectorSpace
infix 4 .+^, .-^, .-.
class AdditiveGroup (Diff p) => AffineSpace p where
  
  type Diff p
  
  (.-.)  :: p -> p -> Diff p
  
  (.+^)  :: p -> Diff p -> p
(.-^) :: AffineSpace p => p -> Diff p -> p
p .-^ v = p .+^ negateV v
distanceSq :: (AffineSpace p, v ~ Diff p, InnerSpace v) =>
              p -> p -> Scalar v
distanceSq = (fmap.fmap) magnitudeSq (.-.)
distance :: (AffineSpace p, v ~ Diff p, InnerSpace v
            , s ~ Scalar v, Floating (Scalar v))
         => p -> p -> s
distance = (fmap.fmap) sqrt distanceSq
alerp :: (AffineSpace p, VectorSpace (Diff p)) =>
         p -> p -> Scalar (Diff p) -> p
alerp p p' s = p .+^ (s *^ (p' .-. p))
instance  AffineSpace Double where
  type Diff Double = Double
  (.-.) =  ()
  (.+^) =  (+)
instance  AffineSpace Float where
  type Diff Float = Float
  (.-.) =  ()
  (.+^) =  (+)
instance Integral a => AffineSpace (Ratio a) where
  type Diff (Ratio a) = Ratio a
  (.-.) = ()
  (.+^) = (+)
instance (AffineSpace p, AffineSpace q) => AffineSpace (p,q) where
  type Diff (p,q)   = (Diff p, Diff q)
  (p,q) .-. (p',q') = (p .-. p', q .-. q')
  (p,q) .+^ (u,v)   = (p .+^ u, q .+^ v)
instance (AffineSpace p, AffineSpace q, AffineSpace r) => AffineSpace (p,q,r) where
  type Diff (p,q,r)      = (Diff p, Diff q, Diff r)
  (p,q,r) .-. (p',q',r') = (p .-. p', q .-. q', r .-. r')
  (p,q,r) .+^ (u,v,w)    = (p .+^ u, q .+^ v, r .+^ w)
instance (AffineSpace p) => AffineSpace (a -> p) where
  type Diff (a -> p) = a -> Diff p
  (.-.)              = liftA2 (.-.)
  (.+^)              = liftA2 (.+^)