{-# LANGUAGE TypeOperators, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.Array.Repa.Arbitrary
        ( -- * Arbitrary Unboxed Arrays
          arbitraryUShaped
        , forAllUShaped
        , forAll2UShaped
        , forAll3UShaped
        , forAll4UShaped
        , forAll5UShaped

          -- * Arbitrary Boxed Arrays
        , arbitraryVShaped
        , forAllVShaped
        , forAll2VShaped
        , forAll3VShaped
        , forAll4VShaped
        , forAll5VShaped)
where
import Data.Array.Repa.Base
import Data.Array.Repa.Repr.Unboxed
import Data.Array.Repa.Shape
import Data.Array.Repa.Index
import Test.QuickCheck.Arbitrary
import Test.QuickCheck.Gen
import Test.QuickCheck.Property                 (forAll)
import Control.Monad
import qualified Data.Array.Repa.Repr.Vector    as V
import qualified Data.Vector.Unboxed            as U


-- Aribrary -------------------------------------------------------------------
-- | This module exports instances of @Arbitrary@ and @CoArbitrary@ for
--   unboxed Repa arrays.
instance Arbitrary Z where
  arbitrary = return Z


-- Note: this is a shape that is "sized", and then random array for a given
-- shape is generated.
instance (Shape a, Arbitrary a) => Arbitrary (a :. Int) where
 arbitrary 
        = sized (\n -> do 
                b       <- choose (1, n)
                let dimLimit :: Int = ceiling (fromIntegral n / fromIntegral b :: Double)
                a       <- resize dimLimit arbitrary
                -- each dimension should be at least 1-wide
                return $ a :. b)


-- | Generates a random unboxed array of a given shape
arbitraryUShaped sh =   fromListUnboxed sh `fmap` vector (size sh)


-- | Generates a random boxed array of a given shape
arbitraryVShaped sh = V.fromListVector  sh `fmap` vector (size sh)


instance (Arbitrary sh, Arbitrary a, U.Unbox a, Shape sh) 
       => Arbitrary (Array U sh a) where
  arbitrary = arbitrary >>= arbitraryUShaped


instance (Arbitrary sh, Arbitrary a, Shape sh) 
       => Arbitrary (Array V.V sh a) where
  arbitrary = arbitrary >>= arbitraryVShaped


-- CoArbitrary ----------------------------------------------------------------
instance CoArbitrary Z where
  coarbitrary _ = id 

instance (Shape a, CoArbitrary a) 
       => CoArbitrary (a :. Int) where
  coarbitrary (a :. b) = coarbitrary a >< coarbitrary b

instance (CoArbitrary sh, CoArbitrary a, Source r a, Shape sh) 
       => CoArbitrary (Array r sh a) where
  coarbitrary arr 
        = (coarbitrary . extent $ arr) >< (coarbitrary . toList $ arr)


-- Wrappers -------------------------------------------------------------------
-- | Convenience functions for writing tests on 2-,3-,4-tuples of arrays
--   of the same size (or just of a fixed size.)

-- | These are helper functions:
forAll2 arbf = forAll $ liftM2 (,)    arbf arbf
forAll3 arbf = forAll $ liftM3 (,,)   arbf arbf arbf
forAll4 arbf = forAll $ liftM4 (,,,)  arbf arbf arbf arbf
forAll5 arbf = forAll $ liftM5 (,,,,) arbf arbf arbf arbf arbf


-- | Property tested for unboxed random arrays with a given shape.
forAllUShaped sh  = forAll  $ arbitraryUShaped sh

-- | Property tested for pair of unboxed random arrays with a given shape.
forAll2UShaped sh = forAll2 $ arbitraryUShaped sh

-- | Property tested for triple of unboxed random arrays with a given shape.
forAll3UShaped sh = forAll3 $ arbitraryUShaped sh

-- | Property tested for quadruple of unboxed random arrays with a given shape.
forAll4UShaped sh = forAll4 $ arbitraryUShaped sh

-- | Property tested for 5-tuple of unboxed random arrays with a given shape.
forAll5UShaped sh = forAll5 $ arbitraryUShaped sh


-- | Property tested for unboxed random arrays with a given shape.
forAllVShaped sh  = forAll  $ arbitraryVShaped sh

-- | Property tested for pair of unboxed random arrays with a given shape.
forAll2VShaped sh = forAll2 $ arbitraryVShaped sh

-- | Property tested for triple of unboxed random arrays with a given shape.
forAll3VShaped sh = forAll3 $ arbitraryVShaped sh

-- | Property tested for quadruple of unboxed random arrays with a given shape.
forAll4VShaped sh = forAll4 $ arbitraryVShaped sh

-- | Property tested for 5-tuple of unboxed random arrays with a given shape.
forAll5VShaped sh = forAll5 $ arbitraryVShaped sh