{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
module Numeric.Quaternion.Class
    ( Quaternion (..)
    ) where

import Numeric.Matrix (Matrix)
import Numeric.Vector (Vector)

-- | Quaternion operations
class Quaternion t where
    -- | Quaternion data type. The ordering of coordinates is @x y z w@,
    --   where @w@ is an argument, and @x y z@ are components of a 3D vector
    data Quater t
    -- | Set the quaternion in format (x,y,z,w)
    packQ :: t -> t -> t -> t -> Quater t
    -- | Get the values of the quaternion in format (x,y,z,w)
    unpackQ :: Quater t -> (t,t,t,t)
    -- | Set the quaternion from 3D axis vector and argument
    fromVecNum :: Vector t 3 -> t -> Quater t
    -- | Set the quaternion from 4D vector in format (x,y,z,w)
    fromVec4 :: Vector t 4 -> Quater t
    -- | Transform the quaternion to 4D vector in format (x,y,z,w)
    toVec4 :: Quater t -> Vector t 4
    -- | Get scalar square of the quaternion.
    --
    --   >> realToFrac (square q) == q * conjugate q
    square :: Quater t -> t
    -- | Imagine part of quaternion (orientation vector)
    im :: Quater t -> Quater t
    -- | Real part of the quaternion
    re :: Quater t -> Quater t
    -- | Get imagenery 3D vector of the quaternion
    imVec :: Quater t -> Vector t 3
    -- | Real part of the quaternion into number
    taker :: Quater t -> t
    -- | i-th component
    takei :: Quater t -> t
    -- | j-th component
    takej :: Quater t -> t
    -- | k-th component
    takek :: Quater t -> t
    -- | Conjugate quaternion (negate imaginary part)
    conjugate :: Quater t -> Quater t
    -- | Rotates and scales vector in 3D using quaternion.
    --   Let @q = (cos (a\/2), sin (a\/2) * v)@; then the rotation angle is @a@, and the axis of rotation is @v@.
    --   Scaling is proportional to @|v|^2@.
    --
    --   >>> rotScale q x == q * x * (conjugate q)
    rotScale :: Quater t -> Vector t 3 -> Vector t 3
    -- | Creates a quaternion @q@ from two vectors @a@ and @b@.
    --   @rotScale q a == b@
    getRotScale :: Vector t 3 -> Vector t 3 -> Quater t
    -- | Creates a rotation versor from an axis vector and an angle in radians.
    --   Result is always a unit quaternion (versor).
    --   If the argument vector is zero, then result is a real unit quaternion.
    axisRotation :: Vector t 3 -> t -> Quater t
    -- | Quaternion rotation angle
    --
    --   >>> q /= 0 ==> axisRotation (imVec q) (qArg q) == signum q
    qArg :: Quater t -> t
    -- | Create a quaternion from a rotation matrix.
    --   Note, that rotations of `q` and `-q` are equivalent, there result of this function may be
    --   ambiguious. I decided to force its real part be positive:
    --
    --   >>> taker (fromMatrix33 m) >= 0
    fromMatrix33 :: Matrix t 3 3 -> Quater t
    -- | Create a quaternion from a homogenious coordinates trasform matrix.
    --   Ignores matrix translation transform.
    --   Note, that rotations of `q` and `-q` are equivalent, there result of this function may be
    --   ambiguious. I decided to force its real part be positive:
    --
    --   >>> taker (fromMatrix44 m) >= 0
    fromMatrix44 :: Matrix t 4 4 -> Quater t
    -- | Create a rotation matrix from a quaternion.
    --   Note, that rotations of `q` and `-q` are equivalent, so the following property holds:
    --
    --   >>> toMatrix33 q == toMatrix33 (-q)
    toMatrix33 :: Quater t -> Matrix t 3 3
    -- | Create a homogenious coordinates trasform matrix from a quaternion.
    --   Translation of the output matrix is zero.
    --   Note, that rotations of `q` and `-q` are equivalent, so the following property holds:
    --
    --   >>> toMatrix44 q == toMatrix44 (-q)
    toMatrix44 :: Quater t -> Matrix t 4 4