packages feed

linear-tests-0.1.1.0: test/Linear/QuaternionSpec.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ExplicitForAll      #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}


module Linear.QuaternionSpec (
  spec
) where

import           Test.Hspec
import           Test.QuickCheck

import           Linear.Conjugate
import           Linear.Epsilon
import           Linear.Metric
import qualified Linear.Quaternion           as Q
import qualified Linear.V3                   as V

import           Linear.Quaternion.Arbitrary
import           Linear.V3.Arbitrary

-- | tests for the property `|(q*v)·v| = 1 or 0`
prop_CartesianQuaternion_arbitrary :: (Show a, Epsilon a, RealFloat a, Conjugate a) => CartesianQuaternion a -> CartesianUnitV3 a -> Bool
prop_CartesianQuaternion_arbitrary (CartesianQuaternion q) (CartesianUnitV3 v) = nearZero qvv || nearZero (qvv - 1) where
  qvv = abs $ Q.rotate q v `dot` v

-- | tests for the property `|q| = 1`
prop_Quaternion_arbitrary :: (Epsilon a, RealFloat a, Conjugate a) => Q.Quaternion a -> Bool
prop_Quaternion_arbitrary q = nearZero $ norm q - 1

-- | tests for the property `q * conjugate(q) = identity`
prop_Quaternion_conjugate :: (Epsilon a, RealFloat a, Conjugate a) => Q.Quaternion a -> Bool
prop_Quaternion_conjugate q = nearZero $ distance (q * conjugate q) (Q.Quaternion 1 (V.V3 0 0 0))

identityQuaternion :: (RealFloat a) => Q.Quaternion a
identityQuaternion = 1 -- Quaternion 1 (V3 0 0 0)

spec :: Spec
spec = specTyped @Double

specTyped :: forall a. (Eq a, Show a, Arbitrary a, Epsilon a, RealFloat a, Conjugate a) => Spec
specTyped = do
  describe "Quaternion" $ do
    describe "Arbitrary" $ do
      describe "Quaternion" $ do
        it "satisfies the property `|q| = 1`" $
          property $ prop_Quaternion_arbitrary @a
      describe "CartesianQuaternion" $ do
        it "satisfies the property `|(q*v)·v| = 1 or 0`" $
          property $ prop_CartesianQuaternion_arbitrary @a
    describe "Conjugate" $ do
      it "satisfies the property `q * conjugate(q) = identity`" $
        property $ prop_Quaternion_conjugate @a