```{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
----------------------------------------------------------------------
-- |
-- Module      :  Data.AffineSpace
-- Copyright   :  (c) Conal Elliott and Andy J Gill 2008
-- License     :  BSD3
--
-- Maintainer  :  conal@conal.net, andygill@ku.edu
-- Stability   :  experimental
--
-- Affine spaces.
----------------------------------------------------------------------

module Data.AffineSpace
(
AffineSpace(..), (.-^), distanceSq, distance, alerp
) where

import Data.VectorSpace

infix 4 .+^, .-^, .-.

class VectorSpace v s => AffineSpace p v s | p -> v s where
-- | Subtract points
(.-.)  :: p -> p -> v
-- | Point plus vector
(.+^)  :: p -> v -> p

-- TODO: consider replacing p and v with a type constructor argument:
--
-- class VectorSpace' v => AffineSpace p v | p -> v where
--   (.-.)  :: p s -> p s -> v s
--   (.+^)  :: p s -> v s -> p s
--
-- Perhaps with constraints on s.  We couldn't then define instances for
-- doubles & floats.

-- | Point minus vector
(.-^) :: (Num s, AffineSpace p v s) => p -> v -> p
p .-^ v = p .+^ negateV v

-- | Square of the distance between two points.  Sometimes useful for
distanceSq :: (AffineSpace p v s, InnerSpace v s) => p -> p -> s
distanceSq = (fmap.fmap) magnitudeSq (.-.)

-- | Distance between two points.  See also 'distanceSq'.
distance :: (Floating s, AffineSpace p v s, InnerSpace v s) => p -> p -> s
distance = (fmap.fmap) sqrt distanceSq

-- | Affine linear interpolation.  Varies from @p@ to @p'@ as @s@ varies
-- from 0 to 1.  See also 'lerp' (on vector spaces).
alerp :: AffineSpace p v s => p -> p -> s -> p
alerp p p' s = p .+^ (s *^ (p' .-. p))

instance  AffineSpace Double Double Double where
(.-.)  =  (-)
(.+^)  =  (+)

instance  AffineSpace Float Float Float where
(.-.)  =  (-)
(.+^)  =  (+)
```