{- |
  General functions applicable to all vector types.
-}

module Data.Vector.Class where

-- | The type of vector field values.
type Scalar = Double

{- |
  All vector types belong to this class. Aside from 'vpack' and 'vunpack', these methods aren't especially useful to end-users; they're used internally by the vector arithmetic implementations.
-}
class BasicVector v where
  -- | Apply a function to all vector fields.
  vmap    :: (Scalar -> Scalar) -> (v -> v)

  -- | Zip two vectors together field-by-field using the supplied function (in the style of @Data.List.zipWith@).
  vzip    :: (Scalar -> Scalar -> Scalar) -> (v -> v -> v)

  -- | Reduce a vector down to a single value using the supplied binary operator. The ordering in which this happens isn't guaranteed, so the operator should probably be associative and commutative.
  vfold   :: (Scalar -> Scalar -> Scalar) -> (v -> Scalar)

  -- | Pack a list of values into a vector. Extra values are ignored, too few values yields @Nothing@.
  vpack   :: [Scalar] -> Maybe v

  -- | Unpack a vector into a list of values. (Always succeeds.)
  vunpack :: v -> [Scalar]

  -- | Convert a 'Scalar' to a vector (with all components the same).
  vpromote :: Scalar -> v

{- |
  Dummy class that enables you to request a vector in a type signature without needing to explicitly list 'Num' or 'Fractional' as well.
-}
class (BasicVector v, Num v, Fractional v) => Vector v where

{- |
  Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(*)@ operator.

  The @(*|)@ and @(|*)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part.
-}
(*|) :: Vector v => Scalar -> v -> v
Scalar
k *| :: forall v. Vector v => Scalar -> v -> v
*| v
v = (Scalar -> Scalar) -> v -> v
forall v. BasicVector v => (Scalar -> Scalar) -> v -> v
vmap (Scalar
kScalar -> Scalar -> Scalar
forall a. Num a => a -> a -> a
*) v
v

{- |
  Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(*)@ operator.

  The @(*|)@ and @(|*)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part.
-}
(|*) :: Vector v => v -> Scalar -> v
v
v |* :: forall v. Vector v => v -> Scalar -> v
|* Scalar
k = (Scalar -> Scalar) -> v -> v
forall v. BasicVector v => (Scalar -> Scalar) -> v -> v
vmap (Scalar
kScalar -> Scalar -> Scalar
forall a. Num a => a -> a -> a
*) v
v

{- |
  Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(/)@ operator.

  The @(/|)@ and @(|/)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part.
-}
(|/) :: Vector v => v -> Scalar -> v
v
v |/ :: forall v. Vector v => v -> Scalar -> v
|/ Scalar
k = v
v v -> Scalar -> v
forall v. Vector v => v -> Scalar -> v
|* (Scalar
1Scalar -> Scalar -> Scalar
forall a. Fractional a => a -> a -> a
/Scalar
k)

{- |
  Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(/)@ operator.

  The @(/|)@ and @(|/)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part.
-}
(/|) :: Vector v => Scalar -> v -> v
Scalar
k /| :: forall v. Vector v => Scalar -> v -> v
/| v
v = (Scalar
1Scalar -> Scalar -> Scalar
forall a. Fractional a => a -> a -> a
/Scalar
k) Scalar -> v -> v
forall v. Vector v => Scalar -> v -> v
*| v
v

infixl 7 *|
infixl 7 |*
infixl 7 /|
infixl 7 |/

-- | Take the /dot product/ of two vectors. This is a scalar equal to the cosine of the angle between the two vectors multiplied by the length of each vectors.
vdot :: Vector v => v -> v -> Scalar
v
v1 vdot :: forall v. Vector v => v -> v -> Scalar
`vdot` v
v2 = (Scalar -> Scalar -> Scalar) -> v -> Scalar
forall v.
BasicVector v =>
(Scalar -> Scalar -> Scalar) -> v -> Scalar
vfold Scalar -> Scalar -> Scalar
forall a. Num a => a -> a -> a
(+) (v -> Scalar) -> v -> Scalar
forall a b. (a -> b) -> a -> b
$ (Scalar -> Scalar -> Scalar) -> v -> v -> v
forall v.
BasicVector v =>
(Scalar -> Scalar -> Scalar) -> v -> v -> v
vzip Scalar -> Scalar -> Scalar
forall a. Num a => a -> a -> a
(*) v
v1 v
v2

-- | Return the length or /magnitude/ of a vector. (Note that this involves a slow square root operation.)
vmag :: Vector v => v -> Scalar
vmag :: forall v. Vector v => v -> Scalar
vmag v
v = Scalar -> Scalar
forall a. Floating a => a -> a
sqrt (v
v v -> v -> Scalar
forall v. Vector v => v -> v -> Scalar
`vdot` v
v)

-- | Normalise a vector. In order words, return a new vector with the same direction, but a length of exactly one. (If the vector's length is zero or very near to zero, the vector is returned unchanged.)
vnormalise :: Vector v => v -> v
vnormalise :: forall v. Vector v => v -> v
vnormalise v
v =
  let m :: Scalar
m = v -> Scalar
forall v. Vector v => v -> Scalar
vmag v
v
  in  if Scalar
m Scalar -> Scalar -> Bool
forall a. Ord a => a -> a -> Bool
< Scalar
1e-16 then v
v else v
v v -> Scalar -> v
forall v. Vector v => v -> Scalar -> v
|* (Scalar
1Scalar -> Scalar -> Scalar
forall a. Fractional a => a -> a -> a
/Scalar
m)

{- |
  Linearly interpolate between two points in space.

  * @vlinear 0 a b = a@

  * @vlinear 1 a b = b@

  * @vlinear 0.5 a b@ would give a point exactly half way between @a@ and @b@ in a straight line.
-}
vlinear :: (Vector v) => Scalar -> v -> v -> v
vlinear :: forall v. Vector v => Scalar -> v -> v -> v
vlinear Scalar
t v
a v
b = (Scalar
1Scalar -> Scalar -> Scalar
forall a. Num a => a -> a -> a
-Scalar
t) Scalar -> v -> v
forall v. Vector v => Scalar -> v -> v
*| v
a v -> v -> v
forall a. Num a => a -> a -> a
+ Scalar
t Scalar -> v -> v
forall v. Vector v => Scalar -> v -> v
*| v
b