module Bayes.PrivateTypes( 
 
   BayesianDiscreteVariable(..)
 , Set(..)
 
 , DV(..)
 , DVSet(..)
 , DVISet(..)
 , TDV
 , tdv
 , tdvi
 
 , Instantiable(..)
 , DVI(..)
 , setDVValue
 , instantiationValue
 , instantiationVariable
 , fromDVSet
 
 , Vertex(..)
 , Edge(..)
 , SimpleGraph(..)
 , DE(..)
 , UE(..)
 
 , getMinBound
 
 , MultiIndex(..)
 , forAllInstantiations 
 , indicesForDomain
 , instantiationDetails
 , instantiation
 , allInstantiationsForOneVariable
 
 , instantiationProp
 ) where
import qualified Data.List as L 
import qualified Data.Vector.Unboxed as V
import Test.QuickCheck
import Test.QuickCheck.Arbitrary
import System.Random(Random)
import qualified Data.IntMap as IM
import qualified Data.Map as M
class Set s where
    
    emptySet :: s a
    
    union :: Eq a => s a -> s a -> s a
    
    intersection :: Eq a => s a -> s a -> s a
    
    difference :: Eq a => s a -> s a -> s a
    
    isEmpty :: s a -> Bool
    
    isElem :: Eq a => a -> s a -> Bool
    
    addElem :: Eq a => a -> s a -> s a
    
    nbElements :: s a -> Int
    
    subset :: Eq a => s a -> s a -> Bool
    
    equal :: Eq a => s a -> s a -> Bool
    equal sa sb = (sa `subset` sb) && (sb `subset` sa)
instance Set [] where
    emptySet = []
    union = L.union
    intersection = L.intersect
    difference a b = a L.\\ b
    isEmpty [] = True 
    isEmpty _ = False
    isElem = L.elem 
    addElem a l = if a `elem` l then l else a:l
    nbElements = length
    subset sa sb = all (`elem` sb) sa
newtype Vertex = Vertex {vertexId :: Int} deriving(Eq,Ord)
data Edge = Edge !Vertex !Vertex deriving(Eq,Ord,Show)
data SimpleGraph local edgedata vertexdata = SP {
 
   edgeMap :: !(M.Map Edge edgedata) 
 
 , vertexMap :: !(IM.IntMap (local, vertexdata))
 
 
 
 , nameMap :: !(IM.IntMap String)
 } 
data DE = DE ![Edge] ![Edge] deriving(Eq,Show)
data UE = UE ![Edge] deriving(Eq,Show)
instance Show Vertex where 
    show (Vertex v) = "v" ++ show v
class BayesianDiscreteVariable v where
    dimension :: v -> Int 
    dv :: v -> DV
    vertex :: v -> Vertex
getMinBound :: Bounded a => a -> a 
getMinBound _ = minBound
data DV = DV !Vertex !Int deriving(Eq,Ord)
newtype DVSet s = DVSet [DV] deriving(Eq,Show)
fromDVSet :: DVSet s -> [DV]
fromDVSet (DVSet l) = l
instance Show DV where
    show (DV v d) = show v ++ "(" ++ show d ++ ")"
instance BayesianDiscreteVariable DV where
    dimension (DV _ d) = d
    dv = id
    vertex (DV v _) = v
data TDV s = TDV !Vertex !Int deriving(Eq,Ord)
instance Show (TDV s) where
    show (TDV v d) = show v
instance BayesianDiscreteVariable (TDV s) where
    dimension (TDV _ d) = d
    dv (TDV v nb) = DV v nb
    vertex (TDV v _) = v
tdv :: DV -> TDV s 
tdv (DV v nb) = TDV v nb
tdvi :: Enum s => DVI -> (TDV s,s)
tdvi (DVI dv value) = (tdv dv, toEnum value)
newtype MultiIndex s = MultiIndex (V.Vector Int) deriving(Eq,Show)
instantiation :: DVSet s -> MultiIndex s -> [DVI]
instantiation (DVSet l) (MultiIndex v) = zipWith setDVValue l (V.toList v)
indicesForDomain :: DVSet s -> [MultiIndex s]
indicesForDomain (DVSet l) = map (MultiIndex . V.fromList) $ (mapM indicesForOneDomain l)
 where 
 	indicesForOneDomain (DV _ d) = [0..d1]
allInstantiationsForOneVariable :: DV -> [DVI]
allInstantiationsForOneVariable v@(DV _ d) = map (setDVValue v) [0..d1]
forAllInstantiations :: DVSet s -> [[DVI]]
forAllInstantiations (DVSet l) = mapM allInstantiationsForOneVariable l
 
data DVI = DVI DV !Int deriving(Eq)
instance Show (DVI) where 
   show (DVI (DV v _) i) = show v ++ "=" ++ show i
   
type DVISet = [DVI]
class Instantiable d v where 
  
  
  (=:) :: d -> v -> DVI 
instance (Bounded b, Enum b) => Instantiable DV b where
  (=:) a b = setDVValue a (fromEnum b  fromEnum (getMinBound b))
instance (Bounded b, Enum b) => Instantiable (TDV b) b where
  (=:) (TDV v nb) b = setDVValue (DV v nb) (fromEnum b  fromEnum (getMinBound b))
setDVValue :: DV -> Int -> DVI
setDVValue v a = DVI v a
instance BayesianDiscreteVariable DVI where
    dimension (DVI v _) = dimension v
    dv = instantiationVariable
    vertex (DVI dv _) = vertex dv
instantiationDetails :: [DVI] -> (DVSet s, MultiIndex s)
instantiationDetails l = (DVSet $ map instantiationVariable l, MultiIndex . V.fromList . map (instantiationValue) $ l)
instantiationValue (DVI _ v) = v
instantiationVariable (DVI dv _) = dv
quickCheckVertexSize :: Int -> Int
quickCheckVertexSize 0 = 2
quickCheckVertexSize 1 = 2
quickCheckVertexSize 2 = 2
quickCheckVertexSize _ = 2
whileIn :: (Arbitrary a, Eq a) => [a] -> Gen a -> Gen a
whileIn l m = do 
    newVal <- m 
    if newVal `elem` l 
        then
            whileIn l m 
        else 
            return newVal
generateWithoutReplacement :: (Random a, Arbitrary a, Eq a)  
                           => Int 
                           -> (a,a) 
                           -> Gen [a]
generateWithoutReplacement n b | n == 1 = generateSingle b 
                               | n > 1 = generateMultiple n b 
                               | otherwise = return []
 where
   generateSingle b = do 
       r <- choose b
       return [r]
   generateMultiple n b = do 
       l <- generateWithoutReplacement (n1) b
       newelem <- whileIn l $ choose b
       return (newelem:l)
instantiationProp :: DVSet s -> Bool 
instantiationProp dvl = 
    let dvs = DVSet (fromDVSet dvl) 
    in 
    forAllInstantiations dvs == map (instantiation dvs) (indicesForDomain dvs) 
instance Arbitrary (DVSet s) where 
    arbitrary =  do
        nbVertex <- choose (1,4) :: Gen Int
        vertexNumbers <- generateWithoutReplacement nbVertex (0,50)
        let dimensions = map (\i -> (DV (Vertex i)  (quickCheckVertexSize i))) vertexNumbers
        return (DVSet dimensions)