{-# OPTIONS_GHC -fno-warn-orphans #-}

module Linear.Matrix.Arbitrary (
  InvertibleM33(..)
  , DiagM33(..)
  , InvertibleM44(..)
  , AffineM44(..)
  , InvertibleAffineM44(..)
) where

import           Control.Lens

import           Linear.Epsilon
import           Linear.Matrix
import qualified Linear.V3           as V3
import qualified Linear.V4           as V4
import           Test.QuickCheck

import           Linear.V3.Arbitrary ()
import           Linear.V4.Arbitrary ()


-- | `Arbitrary InvertibleM33` instances are always invertible
newtype InvertibleM33 a = InvertibleM33 { unInvertibleM33 :: M33 a }  deriving (Show)

instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (InvertibleM33 a) where
  arbitrary = fmap InvertibleM33 $ (V3.V3 <$> arbitrary <*> arbitrary <*> arbitrary) `suchThat` (not . nearZero . det33)

-- | `Arbitrary DiagM33` instances only have non-zero diagonal entries (could still be zero)
newtype DiagM33 a = DiagM33 { unDiagM33 :: M33 a } deriving (Show)

instance (Arbitrary a, Num a) => Arbitrary (DiagM33 a) where
  arbitrary = do
    s1 <- arbitrary
    s2 <- arbitrary
    s3 <- arbitrary
    return . DiagM33 $ V3.V3
      (V3.V3 s1 0 0)
      (V3.V3 0 s2 0)
      (V3.V3 0 0 s3)

newtype InvertibleM44 a = InvertibleM44 { unInvertibleM44 :: M44 a } deriving (Show)

instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (InvertibleM44 a) where
  arbitrary = fmap InvertibleM44 $ (V4.V4 <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary) `suchThat` (not . nearZero . det44)

-- | `Arbitrary AffineM44` instances are affine M44 matrices (i.e. have [0,0,0,1] in last row)
newtype AffineM44 a = AffineM44 { unAffineM44 :: M44 a } deriving (Show)

instance (Arbitrary a, Num a) => Arbitrary (AffineM44 a) where
  arbitrary = do
    r1 <- arbitrary
    r2 <- arbitrary
    r3 <- arbitrary
    return . AffineM44 $ V4.V4 r1 r2 r3 (V4.V4 0 0 0 1)


-- | `Arbitrary InvertibleAffineM44` instances are invertible affine M44 matrices
newtype InvertibleAffineM44 a = InvertibleAffineM44 { unInvertibleAffineM44 :: M44 a } deriving (Show)

instance (Arbitrary a, Num a) => Arbitrary (InvertibleAffineM44 a) where
  arbitrary = do
    m33part <- m33_to_m44 <$> arbitrary
    trans <- arbitrary
    return . InvertibleAffineM44 $ set (V4._w . V4._w) 1 . set translation trans $ m33part