{-# LANGUAGE TypeOperators, FlexibleInstances #-}
{-# OPTIONS -fno-warn-orphans #-}

-- Utils to help with testing. Not exported.
module Data.Array.Repa.Arbitrary
	( arbitraryShape
	, arbitrarySmallShape
	, arbitraryListOfLength
	, arbitrarySmallArray)
where
import Data.Array.Repa.Internals.Elt
import Data.Array.Repa.Internals.Base
import Data.Array.Repa.Index
import Data.Array.Repa.Shape	as S
import Control.Monad
import Test.QuickCheck


-- Arbitrary --------------------------------------------------------------------------------------
instance Arbitrary Z where
	arbitrary	= return Z

-- | Generate an arbitrary index, which may have 0's for some components.
instance (Shape sh, Arbitrary sh) => Arbitrary (sh :. Int)  where
	arbitrary 
	 = do	sh1		<- arbitrary
		let sh1Unit	= if size sh1 == 0 then unitDim else sh1
		
		-- Make sure not to create an index so big that we get
		--	integer overflow when converting it to the linear form.
		n		<- liftM abs $ arbitrary
		let nMax	= maxBound `div` (size sh1Unit)
		let nMaxed	= n `mod` nMax
		
		return	$ sh1 :. nMaxed 

-- | Generate an aribrary shape that does not have 0's for any component.
arbitraryShape 
	:: (Shape sh, Arbitrary sh) 
	=> Gen (sh :. Int)

arbitraryShape 
 = do	sh1		<- arbitrary
	let sh1Unit	= if size sh1 == 0 then unitDim else sh1

	-- Make sure not to create an index so big that we get
	--	integer overflow when converting it to the linear form.
	n		<- liftM abs $ arbitrary
	let nMax	= maxBound `div` size sh1Unit
	let nMaxed	= n `mod` nMax
	let nClamped	= if nMaxed == 0 then 1 else nMaxed
	
	return $ sh1Unit :. nClamped
	
	
-- | Generate an arbitrary shape where each dimension is more than zero, 
--	but less than a specific value.
arbitrarySmallShape 
	:: (Shape sh, Arbitrary sh)
	=> Int
	-> Gen (sh :. Int)

arbitrarySmallShape maxDim
 = do	sh		<- arbitraryShape
	let dims	= listOfShape sh

	let clamp x
		= case x `mod` maxDim of
			0	-> 1
			n	-> n
						
	return	$ if True 
			then shapeOfList $ map clamp dims
			else sh


arbitraryListOfLength 
	:: Arbitrary a
	=> Int -> Gen [a]

arbitraryListOfLength n
	| n == 0		= return []
	| otherwise
	= do	i	<- arbitrary
		rest	<- arbitraryListOfLength (n - 1)
		return	$ i : rest
	
-- | Create an arbitrary small array, restricting the size of each of the
--   dimensions to some value.
arbitrarySmallArray 
	:: (Shape sh, Elt a, Arbitrary sh, Arbitrary a)
	=> Int
	-> Gen (Array (sh :. Int) a)

arbitrarySmallArray maxDim
 = do	sh	<- arbitrarySmallShape maxDim
	xx	<- arbitraryListOfLength (S.size sh)
	return	$ fromList sh xx