-- SG library
-- Copyright (c) 2009, Neil Brown.
-- All rights reserved.
-- 
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
--  * Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
--  * Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
--  * The author's name may not be used to endorse or promote products derived
--    from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


-- | Some types that are very basic vectors.  Most of the use that can be made
-- of the vectors is in their type-class instances, which support a powerful set
-- of operations.  For example:
--
-- > fmap (*3) v -- Scales vector v by 3
-- > pure 0 -- Creates a vector filled with zeroes
-- > v + w -- Adds two vectors (there is a 'Num' instance, basically)
--
-- Plus all the instances for the classes in "Data.SG.Vector", which allows you
-- to use 'getX' and so on.
--
-- You will probably want to create more friendly type synonyms, such as:
--
-- > type Vector2 = Pair Double
-- > type Vector3 = Triple Double
-- > type Line2 = LinePair Double
-- > type Line3 = LineTriple Double
module Data.SG.Vector.Basic where

import Control.Applicative
import Data.Foldable
import Data.Traversable

import Data.SG.Vector

-- | A pair, which acts as a 2D vector.
newtype Pair a = Pair (a, a)
  deriving (Eq, Ord, Show, Read)
-- | A triple, which acts as a 3D vector.
newtype Triple a = Triple (a, a, a)
  deriving (Eq, Ord, Show, Read)
-- | A quad, which acts as a 4D vector.
newtype Quad a = Quad (a, a, a, a)
  deriving (Eq, Ord, Show, Read)

-- | A pair of (position vector, direction vector) to be used as a 2D line.
newtype LinePair a = LinePair (Pair a, Pair a)
  deriving (Eq, Ord, Show, Read)
-- | A pair of (position vector, direction vector) to be used as a 3D line.
newtype LineTriple a = LineTriple (Triple a, Triple a)
  deriving (Eq, Ord, Show, Read)

instance VectorNum Pair where
  fmapNum1 = fmap
  fmapNum1inv = fmap
  fmapNum2 = liftA2
  simpleVec = pure

instance VectorNum Triple where
  fmapNum1 = fmap
  fmapNum1inv = fmap
  fmapNum2 = liftA2
  simpleVec = pure

instance VectorNum Quad where
  fmapNum1 = fmap
  fmapNum1inv = fmap
  fmapNum2 = liftA2
  simpleVec = pure

instance (Show a, Eq a, Num a) => Num (Pair a) where
  (+) = fmapNum2 (+)
  (-) = fmapNum2 (-)
  (*) = fmapNum2 (*)
  abs = fmapNum1inv abs
  signum = fmapNum1 signum
  negate = fmapNum1inv negate
  fromInteger = simpleVec . fromInteger

instance (Show a, Eq a, Num a) => Num (Triple a) where
  (+) = fmapNum2 (+)
  (-) = fmapNum2 (-)
  (*) = fmapNum2 (*)
  abs = fmapNum1inv abs
  signum = fmapNum1 signum
  negate = fmapNum1inv negate
  fromInteger = simpleVec . fromInteger

instance (Show a, Eq a, Num a) => Num (Quad a) where
  (+) = fmapNum2 (+)
  (-) = fmapNum2 (-)
  (*) = fmapNum2 (*)
  abs = fmapNum1inv abs
  signum = fmapNum1 signum
  negate = fmapNum1inv negate
  fromInteger = simpleVec . fromInteger

instance Applicative Pair where
  pure a = Pair (a, a)
  (<*>) (Pair (fa, fb)) (Pair (a, b)) = Pair (fa a, fb b)

instance Foldable Pair where
  foldr f t (Pair (x, y)) = x `f` (y `f` t)

instance Traversable Pair where
  traverse f (Pair (x, y)) = Pair <$> liftA2 (,) (f x) (f y)

instance Applicative Triple where
  pure a = Triple (a, a, a)
  (<*>) (Triple (fa, fb, fc)) (Triple (a, b, c)) = Triple (fa a, fb b, fc c)

instance Foldable Triple where
  foldr f t (Triple (x, y, z)) = x `f` (y `f` (z `f` t))

instance Traversable Triple where
  traverse f (Triple (x, y, z)) = Triple <$> liftA3 (,,) (f x) (f y) (f z)

instance Applicative Quad where
  pure a = Quad (a, a, a, a)
  (<*>) (Quad (fa, fb, fc, fd)) (Quad (a, b, c, d))
    = Quad (fa a, fb b, fc c, fd d)

instance Foldable Quad where
  foldr f t (Quad (x, y, z, a)) = x `f` (y `f` (z `f` (a `f` t)))

instance Traversable Quad where
  traverse f (Quad (x, y, z, a)) = Quad <$> ((,,,) <$> f x <*> f y <*> f z <*> f a)


instance Functor Pair where
  fmap = fmapDefault

instance Functor Triple where
  fmap = fmapDefault

instance Functor Quad where
  fmap = fmapDefault

instance Coord Pair where
  getComponents (Pair (a, b)) = [a, b]
  fromComponents (a:b:_) = Pair (a, b)
  fromComponents xs = fromComponents $ xs ++ repeat 0

instance Coord2 Pair where
  getX (Pair (a, _)) = a
  getY (Pair (_, b)) = b

instance Coord Triple where
  getComponents (Triple (a, b, c)) = [a, b, c]
  fromComponents (a:b:c:_) = Triple (a, b, c)
  fromComponents xs = fromComponents $ xs ++ repeat 0

instance Coord2 Triple where
  getX (Triple (a, _, _)) = a
  getY (Triple (_, b, _)) = b

instance Coord3 Triple where
  getZ (Triple (_, _, c)) = c


instance Coord Quad where
  getComponents (Quad (a, b, c, d)) = [a, b, c, d]
  fromComponents (a:b:c:d:_) = Quad (a, b, c, d)
  fromComponents xs = fromComponents $ xs ++ repeat 0

instance Coord2 Quad where
  getX (Quad (a, _, _, _)) = a
  getY (Quad (_, b, _, _)) = b

instance Coord3 Quad where
  getZ (Quad (_, _, c, _)) = c