```{- |
This module provides several small vectors over @Double@ values.
All fields are strict and unpacked, so using these should be
fairly efficient. Each size of vector is a seperate type. It also
provides a few vector constants to save you some typing now and
then.
-}

module Data.Vector where

{- | The type of @Vector@ fields. -}
type Scalar = Double

{- |
The @Vector@ class. All vectors are members of this class,
and it provides ways to apply functions over vectors.
Typically this methods aren't used directly; rather, the
other class instances for each vector are implemented
in terms of these.
-}
class Vector v where
fromScalar :: Scalar -> v
vmap       :: (Scalar -> Scalar) -> v -> v
vzip       :: (Scalar -> Scalar -> Scalar) -> v -> v -> v
vfold      :: (Scalar -> Scalar -> Scalar) -> v -> Scalar

{- |
Takes the /dot product/ of two vectors [of the same dimension].
If you remember your highschool linear algebra, the dot product
of two vectors V and W is equal to |V| * |W| * cos k, where
|V| is the length of vector V, and k is the minimum angle
between the two vectors.
-}
vdot :: Vector v => v -> v -> Scalar
vdot v w = vfold (+) \$ vzip (*) v w

{- |
Returns the /magnitude/ of a vector (that is, it's length).
Note that this is always positive or zero (never negative).
-}
vmag :: Vector v => v -> Scalar
vmag v = sqrt \$ v `vdot` v

{- |
Multiply a vector by a scalar. This scales the magnitude
(length) of the vector, but leaves its length unchanged.
(Except in the case of a negative scalar, in which case
the vector's direction is reversed.)

The operators '|*' and '*|' are identical, just with their
arguments flipped.
-}
(|*) :: Vector v => Scalar -> v -> v
s |* v = vmap (*s) v

{- |
Multiply a vector by a scalar. This scales the magnitude
(length) of the vector, but leaves its length unchanged.
(Except in the case of a negative scalar, in which case
the vector's direction is reversed.)

The operators '*|' and '|*' are identical, just with their
arguments flipped.
-}
(*|) :: Vector v => v -> Scalar -> v
v *| s = vmap (*s) v

{- |
Adjust a vector so that its length is exactly one. (Or,
if the vector's length was zero, it stays zero.)
-}
vnormalise :: Vector v => v -> v
vnormalise v =
let m = vmag v
in  if m < 1e-10 then v else v *| (1/m)

{- |
The type of 2-dimensional vectors. It provides various
class instances such as 'Eq', 'Num', 'Show', etc.
-}
data Vector2 = Vector2 {v2x, v2y :: {-# UNPACK #-} !Scalar}
deriving (Eq, Ord, Show)

instance Vector Vector2 where
fromScalar x = Vector2 x x
vmap  f (Vector2 x1 y1)                 = Vector2 (f x1)    (f y1)
vzip  f (Vector2 x1 y1) (Vector2 x2 y2) = Vector2 (f x1 x2) (f y1 y2)
vfold f (Vector2 x1 y1)                 = f x1 y1

instance Num Vector2 where
(+) = vzip (+)
(-) = vzip (-)
(*) = vzip (*)
negate = vmap negate
abs    = vmap abs
signum = vmap signum
fromInteger n = let s = fromInteger n in fromScalar s

instance Fractional Vector2 where
(/) = vzip (/)
recip = vmap recip
fromRational r = let s = fromRational r in fromScalar s

-- | Constant: The unit-length X vector, (1, 0).
vector2X :: Vector2
vector2X = Vector2 1 0

-- | Constant: The unit-length Y vector, (0, 1).
vector2Y :: Vector2
vector2Y = Vector2 0 1

{- |
The type of 3-dimensional vectors. Similar to 'Vector2'.
-}
data Vector3 = Vector3 {v3x, v3y, v3z :: {-# UNPACK #-} !Scalar}
deriving (Eq, Ord, Show)

instance Vector Vector3 where
fromScalar x = Vector3 x x x
vmap  f (Vector3 x1 y1 z1)                    = Vector3 (f x1)    (f y1)    (f z1)
vzip  f (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) = Vector3 (f x1 x2) (f y1 y2) (f z1 z2)
vfold f (Vector3 x1 y1 z1)                    = f x1 (f y1 z1)

instance Num Vector3 where
(+) = vzip (+)
(-) = vzip (-)
(*) = vzip (*)
negate = vmap negate
abs    = vmap abs
signum = vmap signum
fromInteger n = let s = fromInteger n in fromScalar s

instance Fractional Vector3 where
(/) = vzip (/)
recip = vmap recip
fromRational r = let s = fromRational r in fromScalar s

{- |
Takes the /cross product/ of two [3D] vectors. Again, from highschool
linear algebra, the cross product of vector V and W is a new vector
P such that |P| = |V| * |W| * sin k (where k is the minimum angle
between V and W), and the direction of P is perpendicular to both
V and W. For example, @vcross 'vector3X' 'vector3Y' = 'vector3Z'@.
Note also that @vcross w v = negate (vcross v w)@.
-}
vcross :: Vector3 -> Vector3 -> Vector3
vcross (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) = Vector3
{
v3x = y1 * z2   -   y2 * z1,
v3y = x1 * z2   -   x2 * z1,
v3z = x1 * y2   -   x2 * y1
}

-- | Constant: The unit-length X vector, (1, 0, 0).
vector3X :: Vector3
vector3X = Vector3 1 0 0

-- | Constant: The unit-length Y vector, (0, 1, 0).
vector3Y :: Vector3
vector3Y = Vector3 0 1 0

-- | Constant: The unit-length Z vector, (0, 0, 1).
vector3Z :: Vector3
vector3Z = Vector3 0 0 1
```