module Generics.RepLib.Lib (
  
  subtrees, deepSeq, rnf,
  
  GSum(..),
  Zero(..),
  Generate(..),
  Enumerate(..),
  Shrink(..),
  Lreduce(..),
  Rreduce(..),
  
  Fold(..),
  crush, gproduct, gand, gor, flatten, count, comp, gconcat, gall, gany, gelem,
  
  GSumD(..), ZeroD(..), GenerateD(..), EnumerateD(..), ShrinkD(..), LreduceD(..), RreduceD(..),
  rnfR, deepSeqR, gsumR1, zeroR1, generateR1, enumerateR1, lreduceR1, rreduceR1
) where
import Generics.RepLib.R
import Generics.RepLib.R1
import Generics.RepLib.RepAux
import Generics.RepLib.PreludeReps()
import Generics.RepLib.AbstractReps()
import Control.Applicative (Applicative (..))
import Control.Monad (ap,liftM)
import Data.List (inits)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Map (Map)
import qualified Data.Map as Map
subtrees :: forall a. Rep a => a -> [a]
subtrees x = [y | Just y <- gmapQ (cast :: Query (Maybe a)) x]
deepSeq :: Rep a => a -> b -> b
deepSeq = deepSeqR rep
rnf :: Rep a => a -> a
rnf = rnfR rep
rnfR :: R a -> a -> a
rnfR (Data _ cons) x =
    case (findCon cons x) of
      Val emb reps args -> to emb (map_l rnfR reps args)
rnfR _ x = x
deepSeqR :: R a -> a -> b -> b
deepSeqR (Data _ cons) = \x ->
    case (findCon cons x) of
      Val _ reps args -> foldl_l (\ra bb a -> (deepSeqR ra a) . bb) id reps args
deepSeqR _ = seq
class Rep1 GSumD a => GSum a where
   gsum :: a -> Int
   gsum = gsumR1 rep1
data GSumD a = GSumD { gsumD :: a -> Int }
gsumR1 :: R1 GSumD a -> a -> Int
gsumR1 Int1           x = x
gsumR1 (Arrow1 _ _)   _ = error "urk"
gsumR1 (Data1 _ cons) x =
  case (findCon cons x) of
      Val _ rec kids ->
        foldl_l (\ca a b -> (gsumD ca b) + a) 0 rec kids
gsumR1 _              _ = 0
instance GSum a => Sat (GSumD a) where
   dict = GSumD gsum
instance GSum Float
instance GSum Int
instance GSum Bool
instance GSum ()
instance GSum Integer
instance GSum Char
instance GSum Double
instance (GSum a, GSum b) => GSum (a,b)
instance (GSum a) => GSum [a]
instance (Rep k, GSum a) => GSum (Map k a) where
  gsum = gsum . Map.elems
instance GSum a => GSum (Set a) where
  gsum = gsum . Set.elems
class (Rep1 ZeroD a) => Zero a where
    zero :: a
    zero = zeroR1 rep1
data ZeroD a = ZD { zeroD :: a }
instance Zero a => Sat (ZeroD a) where
    dict = ZD zero
zeroR1 :: R1 ZeroD a -> a
zeroR1 Int1 = 0
zeroR1 Char1 = minBound
zeroR1 (Arrow1 _ z2) = const (zeroD z2)
zeroR1 Integer1 = 0
zeroR1 Float1 = 0.0
zeroR1 Double1 = 0.0
zeroR1 (Data1 _ (Con emb rec : _)) = to emb (fromTup zeroD rec)
zeroR1 IOError1 = userError "Default Error"
zeroR1 r1 = error ("No zero element of type: " ++ show r1)
instance Zero Int
instance Zero Char
instance (Zero a, Zero b) => Zero (a -> b)
instance Zero Integer
instance Zero Float
instance Zero Double
instance Zero IOError
instance Zero ()
instance Zero Bool
instance (Zero a, Zero b) => Zero (a,b)
instance Zero a => Zero [a]
instance (Rep k, Rep a) => Zero (Map k a) where
  zero = Map.empty
instance (Rep a) => Zero (Set a) where
  zero = Set.empty
data GenerateD a = GenerateD { generateD :: Int -> [a] }
class Rep1 GenerateD a => Generate a where
  generate :: Int -> [a]
  generate = generateR1 rep1
instance Generate a => Sat (GenerateD a) where
  dict = GenerateD generate
genEnum :: (Enum a) => Int -> [a]
genEnum d = enumFromTo (toEnum 0) (toEnum d)
generateR1 :: R1 GenerateD a -> Int -> [a]
generateR1 Int1           d = genEnum d
generateR1 Char1          d = genEnum d
generateR1 Integer1       d = genEnum d
generateR1 Float1         d = genEnum d
generateR1 Double1        d = genEnum d
generateR1 (Data1 _ _)    0 = []
generateR1 (Data1 _ cons) d =
  [ to emb l | (Con emb rec) <- cons,
               l <- fromTupM (\x -> generateD x (d1)) rec]
generateR1 r1 _ = error ("No way to generate type: " ++ show r1)
instance Generate Int
instance Generate Char
instance Generate Integer
instance Generate Float
instance Generate Double
instance Generate ()
instance (Generate a, Generate b) => Generate (a,b)
instance Generate a => Generate [a]
instance (Ord a, Generate a) => Generate (Set a) where
  generate i = map Set.fromList (generate i)
instance (Ord k, Generate k, Generate a) => Generate (Map k a) where
  generate 0 = []
  generate i = map Map.fromList
                 (inits [ (k, v) | k <- generate (i1), v <- generate (i1)])
data EnumerateD a = EnumerateD { enumerateD :: [a] }
instance Enumerate a => Sat (EnumerateD a) where
    dict = EnumerateD { enumerateD = enumerate }
class Rep1 EnumerateD a => Enumerate a where
    enumerate :: [a]
    enumerate = enumerateR1 rep1
enumerateR1 :: R1 EnumerateD a -> [a]
enumerateR1 Int1 =  [minBound .. (maxBound::Int)]
enumerateR1 Char1 = [minBound .. (maxBound::Char)]
enumerateR1 (Data1 _ cons) = enumerateCons cons
enumerateR1 r1 = error ("No way to enumerate type: " ++ show r1)
enumerateCons :: [Con EnumerateD a] -> [a]
enumerateCons (Con emb rec:rest) =
  (map (to emb) (fromTupM enumerateD rec)) ++ (enumerateCons rest)
enumerateCons [] = []
instance Enumerate Int
instance Enumerate Char
instance Enumerate Integer
instance Enumerate Float
instance Enumerate Double
instance Enumerate Bool
instance Enumerate ()
instance (Enumerate a, Enumerate b) => Enumerate (a,b)
instance Enumerate a => Enumerate [a]
instance (Ord a, Enumerate a) => Enumerate (Set a) where
   enumerate = map Set.fromList enumerate
instance (Ord k, Enumerate k, Enumerate a) => Enumerate (Map k a) where
   enumerate = map Map.fromList
                 (inits [ (k, v) | k <- enumerate, v <- enumerate])
data ShrinkD a = ShrinkD { shrinkD :: a -> [a] }
instance Shrink a => Sat (ShrinkD a) where
    dict = ShrinkD { shrinkD    = shrink }
class (Rep1 ShrinkD a) => Shrink a where
    shrink :: a -> [a]
    shrink a = subtrees a ++ shrinkStep a
               where shrinkStep _t = let M _ ts = gmapM1 m a
                                     in ts
                     m :: forall b. ShrinkD b -> b -> M b
                     m d x = M x (shrinkD d x)
data M a = M a [a]
instance Functor M where
  fmap = liftM
instance Applicative M where
  pure x = M x []
  (<*>)  = ap
instance Monad M where
 return x = M x []
 (M x xs) >>= k = M r (rs1 ++ rs2)
   where
     M r rs1 = k x
     rs2 = [r' | x' <- xs, let M r' _ = k x']
instance Shrink Int
instance Shrink a => Shrink [a]
instance Shrink Char
instance Shrink ()
instance (Shrink a, Shrink b) => Shrink (a,b)
instance (Ord a, Shrink a) => Shrink (Set a) where
  shrink x = map Set.fromList (shrink (Set.toList x))
instance (Ord k, Shrink k, Shrink a)  => Shrink (Map k a) where
  shrink m = map Map.fromList (shrink (Map.toList m))
data RreduceD b a = RreduceD { rreduceD :: a -> b -> b }
data LreduceD b a = LreduceD { lreduceD :: b -> a -> b }
class Rep1 (RreduceD b) a => Rreduce b a where
    rreduce :: a -> b -> b
    rreduce = rreduceR1 rep1
class Rep1 (LreduceD b) a => Lreduce b a where
    lreduce :: b -> a -> b
    lreduce = lreduceR1 rep1
instance Rreduce b a => Sat (RreduceD b a) where
    dict = RreduceD { rreduceD = rreduce }
instance Lreduce b a => Sat (LreduceD b a) where
    dict = LreduceD { lreduceD = lreduce }
lreduceR1 :: R1 (LreduceD b) a -> b -> a -> b
lreduceR1 (Data1 _ cons) b a = case (findCon cons a) of
  Val _ rec args -> foldl_l lreduceD b rec args
lreduceR1 _              b _ = b
rreduceR1 :: R1 (RreduceD b) a -> a -> b -> b
rreduceR1 (Data1 _ cons) a b = case (findCon cons a) of
  Val _ rec args -> foldr_l rreduceD b rec args
rreduceR1 _              _ b = b
instance Lreduce b Int
instance Lreduce b ()
instance Lreduce b Char
instance Lreduce b Bool
instance (Lreduce c a, Lreduce c b) => Lreduce c (a,b)
instance Lreduce c a => Lreduce c[a]
instance (Ord a, Lreduce b a) => Lreduce b (Set a) where
  lreduce b a =  (lreduce b (Set.toList a))
instance Rreduce b Int
instance Rreduce b ()
instance Rreduce b Char
instance Rreduce b Bool
instance (Rreduce c a, Rreduce c b) => Rreduce c (a,b)
instance Rreduce c a => Rreduce c[a]
instance (Ord a, Rreduce b a) => Rreduce b (Set a) where
  rreduce a b =  (rreduce (Set.toList a) b)
class Fold f where
  foldRight :: Rep a => (a -> b -> b) -> f a -> b -> b
  foldLeft  :: Rep a => (b -> a -> b) -> b -> f a -> b
crush      :: (Rep a, Fold t) => (a -> a -> a) -> a -> t a -> a
crush op   = foldLeft op
gproduct   :: (Rep a, Num a, Fold t) => t a -> a
gproduct t = foldLeft (*) 1 t
gand       :: (Fold t) => t Bool -> Bool
gand t     = foldLeft (&&) True t
gor        :: (Fold t) => t Bool -> Bool
gor  t     = foldLeft (||) False t
flatten    :: (Rep a, Fold t) => t a -> [a]
flatten t  = foldRight (:) t []
count      :: (Rep a, Fold t) => t a -> Int
count t    = foldRight (const (+1)) t 0
comp       :: (Rep a, Fold t) => t (a -> a) -> a -> a
comp t     = foldLeft (.) id t
gconcat    :: (Rep a, Fold t) => t [a] -> [a]
gconcat t  = foldLeft (++) []  t
gall       :: (Rep a, Fold t) => (a -> Bool) -> t a -> Bool
gall p t   = foldLeft (\a b -> a && p b) True t
gany       :: (Rep a, Fold t) => (a -> Bool) -> t a -> Bool
gany p t   = foldLeft (\a b -> a || p b) False t
gelem      :: (Rep a, Eq a, Fold t) => a -> t a -> Bool
gelem x t  = foldRight (\a b -> a == x || b) t False
instance Fold [] where
  foldRight op = rreduceR1 (rList1 (RreduceD { rreduceD = op })
                           (RreduceD { rreduceD = foldRight op }))
  foldLeft op = lreduceR1 (rList1 (LreduceD  { lreduceD = op })
                          (LreduceD { lreduceD = foldLeft op }))
instance Fold Set where
  foldRight op x b = foldRight op (Set.toList x) b
  foldLeft op b x = foldLeft op b (Set.toList x)
instance Fold (Map k) where
  foldRight op x b = foldRight op (Map.elems x) b
  foldLeft op b x = foldLeft op b (Map.elems x)