-- 
-- (c) Susumu Katayama
--
-- This file is supposed to be used with Version 0.8.5 of MagicHaskeller.
-- For previous versions, try:
-- darcs get http://nautilus.cs.miyazaki-u.ac.jp/~skata/ somedirectoryname
-- and retrieve an older version via some darcs command.
{-# OPTIONS -XTemplateHaskell -XNoMonomorphismRestriction -fglasgow-exts #-}
module MagicHaskeller.LibTH(module MagicHaskeller.LibTH, module MagicHaskeller) where

import MagicHaskeller
import System.Random(mkStdGen)
import Control.Monad(liftM2)

initialize, init075 :: IO ()
initialize = do setPrimitives (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |]))
                setDepth 10
-- MagicHaskeller version 0.8 ignores the setDepth value and always memoizes.

init075 = do setPG $ mkMemo075 (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| ((+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |] ))
             setDepth 10

-- The @tv1@ option prevents type variable @a@ in @forall a. E1(a) -> E2(a) -> ... -> En(a) -> a@ from matching n-ary functions where n>=2.
-- This can safely be used if @(,)@ and @uncurry@ are in the primitive set,
-- because @forall a b c. E1(a->b->c) -> E2(a->b->c) -> ... -> En(a->b->c) -> a -> b -> c@ and @forall a b c. E1((a,b)->c) -> E2((a,b)->c) -> ... -> En((a,b)->c) -> (a,b) -> c@ are isomorphic, and thus the latter can always be used instead of the former.

inittv1 = do setPG $ mkPGOpt (options{primopt = Nothing, tv1 = True})
                             (list ++ nat ++ natural ++ mb ++ bool ++ tuple ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |] ))
             setDepth 10

tuple = $(p [| ((,) :: a -> b -> (a,b), uncurry :: (a->b->c) -> (->) (a,b) c) |])

-- Specialized memoization tables. Choose one for quicker results.
mall, mlist, mlist', mnat, mlistnat, mnat_nc  :: ProgramGenerator pg => pg
mall  = mkPG (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |]))
mlist = mkPG list
mlist' = mkPG list'
mnat  = mkPG (nat ++ $(p [| (+) :: Int -> Int -> Int |]))
mnatural  = mkPG (natural ++ $(p [| (+) :: Integer -> Integer -> Integer |]))
mlistnat = mkPG (list ++ nat ++ $(p [| (+) :: Int -> Int -> Int |]))
mlistnatural = mkPG (list ++ natural ++ $(p [| (+) :: Integer -> Integer -> Integer |]))

mnat_nc = mkMemo (nat ++ $(p [| (+) :: Int -> Int -> Int |]))

hd :: [a] -> Maybe a
hd []    = Nothing
hd (x:_) = Just x

-- Prefixed (->) means that the parameter can be matched as an assumption when 'constrL' option is True. Also, this info is used when 'guess' option is True. For example of maybe :: a -> (b->a) -> (->) (Maybe b) a, 
--   Gamma |- A   Gamma,B |- A
--  ---------------------------maybe
--   Gamma, Maybe B |- A
-- rather than
--   Gamma |- A   Gamma,B |- A   Gamma |- Maybe B
--  -----------------------------------------------maybe
--   Gamma |- A
-- This is just for the efficiency reason, and one can use the infixed form, i.e., maybe :: a -> (b->a) -> Maybe b -> a, if efficiency does not matter. In fact, this info is ignored if both 'guess' and 'constrL' options are False.

mb, nat, natural, list', list, bool, boolean, eq, intinst, list1, list2, list3, nats, tuple, rich, rich', debug :: [Primitive]
mb = $(p [| ( Nothing :: Maybe a, Just :: a -> Maybe a, maybe :: a -> (b->a) -> (->) (Maybe b) a ) |] )

nat = $(p [| (0 :: Int, succ :: Int->Int, nat_para :: (->) Int (a -> (Int -> a -> a) -> a)) |] )
natural = $(p [| (0 :: Integer, succ :: Integer->Integer, nat_para :: (->) Integer (a -> (Integer -> a -> a) -> a)) |] )

-- Nat paramorphism
nat_para :: Integral i => i -> a -> (i -> a -> a) -> a
nat_para i x f = np (abs i) -- Version 0.8 does not deal with partial functions very well.
    where np 0 = x
          np i = let i' = i-1
                 in f i' (np i')

list' = $(p [| ([], (:), foldr :: (b -> a -> a) -> a -> (->) [b] a) |] )
list  = $(p [| ([], (:), list_para :: (->) [b] (a -> (b -> [b] -> a -> a) -> a)) |] )

-- List paramorphism
list_para :: [b] -> a -> (b -> [b] -> a -> a) -> a
list_para []     x f = x
list_para (y:ys) x f = f y ys (list_para ys x f)

bool = $(p [| (True, False, iF :: (->) Bool (a -> a -> a)) |] )

iF :: Bool -> a -> a -> a
iF True  t f = t
iF False t f = f

-- | 'postprocess' replaces uncommon functions like paramorphisms with well-known functions. In future it can do some refactoring.
postprocess :: Exp -> ExpQ
{- This type of patterns is not available yet.
postprocess (AppE (AppE (AppE (VarE 'iF)        p)  t) f) = [| if $(postprocess p) then $(postprocess t) else $(postprocess f) |]
postprocess (AppE (AppE (AppE (VarE 'nat_para)  i)  x) f) = [| let {np 0  = $(postprocess x); np (n+1)  = $(postprocess f) n (np n)}     in np (abs $(postprocess i)) |]
postprocess (AppE (AppE (AppE (VarE 'list_para) xs) x) f) = [| let {lp [] = $(postprocess x); lp (y:ys) = $(postprocess f) y ys (lp ys)} in lp $(postprocess xs) |]
-}
postprocess (AppE (AppE (AppE (VarE name)        p)  t) f)
    = case nameBase name of
        "iF"        -> [| if $(postprocess p) then $(postprocess t) else $(postprocess f) |]
        "nat_para"  -> [| let {np 0  = $(postprocess t); np n  = let i=n-1 in $(postprocess f) i (np i)}     in np (abs $(postprocess p)) |]
        "list_para" -> [| let {lp [] = $(postprocess t); lp (y:ys) = $(postprocess f) y ys (lp ys)} in lp $(postprocess p) |]
postprocess (AppE f x) = [| $(postprocess f) $(postprocess x) |]
-- postprocess (VarE 'iF) = [| \p t f -> if p then t else f |] -- This pattern is actually unnecessary because only eta-long normal expressions will be generated.
-- ...
postprocess (InfixE me1 op me2) = let fmapM f Nothing  = return Nothing
                                      fmapM f (Just x) = fmap Just (f x)
                                  in liftM2 (\e1 e2 -> InfixE e1 op e2) (fmapM postprocess me1) (fmapM postprocess me2)
postprocess (LamE pats e) = fmap (LamE pats) (postprocess e)
postprocess (TupE es) = fmap TupE (mapM postprocess es)
postprocess (ListE es) = fmap ListE (mapM postprocess es)
postprocess (SigE e ty) = fmap (`SigE` ty) (postprocess e)
postprocess e = return e

{-
postprocess :: Exp -> Exp
postprocess (AppE (AppE (AppE (VarE 'iF) p) t) f) = CondE (postprocess p) (postprocess t) (postprocess f)
postprocess (VarE 'iF) = LamE [ VarP n | n <- names ] (CondE p t f)
    where names@[p,t,f] = [ mkName [n] | n <- "ptf" ]
postprocess 
-}

exploit :: (Typeable a, Filtrable a) => (a -> Bool) -> IO ()
exploit pred = filterThenF pred (everything (reallyall::ProgGenSF)) >>= pprs

boolean = $(p [| ((&&) :: Bool -> Bool -> Bool,
                  (||) :: Bool -> Bool -> Bool,
                  not  :: Bool -> Bool) |] )
-- Type classes are not supported yet....
eq = $(p [| ((==) :: Int->Int->Bool,   (/=) :: Int->Int->Bool,
             (==) :: Char->Char->Bool, (/=) :: Char->Char->Bool,
             (==) :: Bool->Bool->Bool, (/=) :: Bool->Bool->Bool,
             (==) :: [Int] ->[Int] ->Bool, (/=) :: [Int] ->[Int]->Bool,
             (==) :: [Char]->[Char]->Bool, (/=) :: [Char]->[Char]->Bool,
             (==) :: [Bool]->[Bool]->Bool, (/=) :: [Bool]->[Bool]->Bool) |] )
-- ...bothered.
intinst = $(p [| ( (<=) :: Int->Int->Bool,
                   (<)  :: Int->Int->Bool,
                   (>=) :: Int->Int->Bool,
                   (>)  :: Int->Int->Bool,
                   max  :: Int->Int->Int,
                   min  :: Int->Int->Int,
                   (-)  :: Int->Int->Int,
                   (*)  :: Int->Int->Int,
                   div  :: Int->Int->Int,
                   mod  :: Int->Int->Int,
                   gcd  :: Int->Int->Int,
                   lcm  :: Int->Int->Int,
                   (^)  :: Int->Int->Int) |])

list1 = $(p [| (map       :: (a -> b) -> (->) [a] [b],
                (++)      :: [a] -> [a] -> [a],
                filter    :: (a -> Bool) -> [a] -> [a],
                concat    :: [[a]] -> [a],
                concatMap :: (a -> [b]) -> (->) [a] [b],
                length    :: (->) [a] Int,
                replicate :: Int -> a -> [a],
                take      :: Int -> [a] -> [a],
                drop      :: Int -> [a] -> [a],
                takeWhile :: (a -> Bool) -> [a] -> [a],
                dropWhile :: (a -> Bool) -> [a] -> [a]) |] )
list2 = $(p [| (
                lines            :: [Char] -> [[Char]],
                words            :: [Char] -> [[Char]],
                unlines            :: [[Char]] -> [Char],
                unwords            :: [[Char]] -> [Char] ) |] )

list3 = $(p [| (reverse :: [a] -> [a],
                and         :: [Bool] -> Bool,
                or          :: [Bool] -> Bool,
                any         :: (a -> Bool) -> (->) [a] Bool,
                all         :: (a -> Bool) -> (->) [a] Bool,
                zipWith          :: (a->b->c) -> (->) [a] ((->) [b] [c]) ) |] )

nats = $(p [| (1 ::Int, 2 :: Int, 3 :: Int) |])

reallyall :: ProgramGenerator pg => pg
reallyall = mkPG rich

nrnds = repeat 5


-- comment out (mkStdGen 123456) when using 0.8.3*


-- Currently only the pg==ConstrLSF case makes sense.
mix, poormix :: ProgramGenerator pg => pg
mix = mkPGSF (mkStdGen 123456)
              nrnds
              (list++bool)
              rich

rich =        (list ++ bool ++
                    -- nat ++
                        -- mb ++ bool ++ $(p [| (hd :: [a] -> Maybe a, (+) :: Int -> Int -> Int) |]) ++
                    boolean ++ eq ++ -- intinst ++
                    list1 ++ list2 ++ list3)


poormix = mkPGSF (mkStdGen 123456)
              nrnds
              $(p [| ([] :: [a], True) |] )
              rich

-- just for debugging
ra :: ProgramGenerator pg => pg
ra = mkPG rich'
rich' =      (list++bool++boolean++
                    list1 ++ list3)

mx :: ProgramGenerator pg => pg
mx = mkPGSF (mkStdGen 123456)
             nrnds
             (list++bool)
             rich'

debug = $(p [| (list_para :: (->) [b] (a -> (b -> [b] -> a -> a) -> a), concatMap :: (a -> [b]) -> (->) [a] [b]) |] )

