{-|
Module      : PhysicsVectors
Description : Implementation of two and three dimensional vectors as used in Physics

-}

{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

module PhysicsVectors
       (
         Vec(..)
       , Vec3(..)
       , Vec2(..)
       ) where

import GHC.Generics (Generic)
import Control.DeepSeq(NFData)


{-|
  The Vec class represents objects embedable within a normed vector space.

  Implementing Vec requires a concept of a dot product and scalar multiplication
  with the restriction that the embedded vector space be over a ring with
  a multiplicative inverse.

  More formally, instances should satisfy the three dot product rules, with the
  'fromScalar' requirement being an explicit representation of the implicit
  scalar multiplication process through a hadamard product.

  * \( x \cdot y = \overline{y \cdot x} \)
  * \( ax \cdot y = a(x \cdot y)\) and \((x + y) \cdot z = x \cdot z + x \cdot y \)
  * \( x \cdot x \geq 0 \)


-}
class (Num a, Floating b, Eq b) => Vec a b | a -> b where
  -- | 'fromScalar' @s@ returns a unit vector scaled by @s@
  fromScalar :: b -> a
  {-| 'dot' @v w@ returns the dot product of @v@ and @w@ as defined by the
     embedded vector space
  -}
  dot :: a -> a -> b
  normSq :: a -> b
  normSq w = dot w w
  norm :: a -> b
  norm = sqrt . normSq
  dist :: a -> a -> b
  dist w w' = norm $ w - w'
  unitVec :: a -> a
  unitVec w = (1/norm w) `scalarMult` w
  scalarMult :: b -> a -> a
  scalarMult 0 _ = fromScalar 0
  scalarMult s w = fromScalar s * w


{-|
  'Vec3' @x y z@ constructs a three dimensional cartesian vector with
  coordinates as noted.
-}
data Vec3 a = Vec3 { x::a, y::a, z::a }
  deriving (Eq, Ord, Show, Generic, NFData)

{-|
  'Vec2' @v1 v2@ constructs a two dimensional column vector of two three
  dimensional vectors of type 'Vec3'
-}
data Vec2 a = Vec2 { r :: Vec3 a, v :: Vec3 a }
  deriving (Eq, Ord, Show, Generic, NFData)

instance (Eq a, Floating a) => Vec (Vec3 a) a where
  fromScalar s = Vec3 s s s
  dot a b = sum [ q a * q b | q <- [x, y, z]]

instance (Eq a, Floating a) => Vec (Vec2 a) a where
  fromScalar s = Vec2 (fromScalar s) (fromScalar s)
  dot a b = sum [ dot (q a) (q b) | q <- [r, v]]

instance Floating a => Num (Vec3 a) where
  (Vec3 x1 y1 z1) + (Vec3 x2 y2 z2) = Vec3 (x1 + x2) (y1 + y2) (z1 + z2)
  (Vec3 x1 y1 z1) * (Vec3 x2 y2 z2) = Vec3 (x1 * x2) (y1 * y2) (z1 * z2)
  (Vec3 x1 y1 z1) - (Vec3 x2 y2 z2) = Vec3 (x1 - x2) (y1 - y2) (z1 - z2)
  abs (Vec3 x1 y1 z1) = Vec3 (abs x1) (abs y1) (abs z1)
  signum (Vec3 x1 y1 z1) = Vec3 (signum x1) (signum y1) (signum z1)
  fromInteger i = Vec3 (fromInteger i) (fromInteger i) (fromInteger i)

instance Floating a => Num (Vec2 a) where
  (Vec2 r1 v1) + (Vec2 r2 v2) = Vec2 (r1 + r2) (v1 + v2)
  (Vec2 r1 v1) * (Vec2 r2 v2) = Vec2 (r1 * r2) (v1 * v2)
  (Vec2 r1 v1) - (Vec2 r2 v2) = Vec2 (r1 - r2) (v1 - v2)
  abs (Vec2 r1 v1) = Vec2 (abs r1) (abs v1)
  signum (Vec2 r1 v1) = Vec2 (signum r1) (signum v1)
  fromInteger i = Vec2 (fromInteger i) (fromInteger i)