```{- |
3-dimensional vectors with vector arithmetic.
-}

module Data.Vector.V3 where

import Data.Vector.Class

data Vector3 = Vector3 {v3x, v3y, v3z :: {-# UNPACK #-} !Scalar} deriving (Eq, Show)

instance BasicVector Vector3 where
vmap  f (Vector3 x  y  z )                    = Vector3 (f x)     (f y)     (f z)
vzip  f (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) = Vector3 (f x1 x2) (f y1 y2) (f z1 z2)
vfold f (Vector3 x  y  z )                    = f x (f y z)

vpack (x:y:z:_) = Just \$ Vector3 x y z
vpack _         = Nothing

vunpack (Vector3 x y z) = [x,y,z]

vpromote x = Vector3 x x x

instance Num Vector3 where
(+) = vzip (+)
(-) = vzip (-)
(*) = vzip (*)
abs = vmap abs
signum = vmap signum
fromInteger = vpromote . fromInteger

instance Fractional Vector3 where
(/) = vzip (/)
recip = vmap recip
fromRational = vpromote . fromRational

instance Vector Vector3 where

{- |
Take the /cross product/ of two 3D vectors. This produces a new 3D vector that is perpendicular to the plane of the first two vectors, and who's length is equal to the sine of the angle between those vectors multiplied by their lengths.

Note that @a \`vcross\` b = negate (b \`vcross\` a)@.
-}
vcross :: Vector3 -> Vector3 -> Vector3
vcross (Vector3 x1 y1 z1) (Vector3 x2 y2 z2) =
Vector3
{
v3x = y1 * z2   -   y2 * z1,
v3y = z1 * x2   -   z2 * x1,
v3z = x1 * y2   -   x2 * y1
}
```