-- 
-- (C) Susumu Katayama
--
{-# LANGUAGE CPP #-}
module MagicHaskeller.Analytical.Synthesize where

import Data.List(transpose)
-- import Control.Monad.Search.RecompDL -- a version using DList, but did not actually improve the efficiency.
import Control.Monad -- hiding (guard)
import Control.Monad.State -- hiding (guard)
import qualified Data.IntMap as IntMap

import Language.Haskell.TH

import Control.Monad.Search.Combinatorial
import MagicHaskeller.CoreLang hiding (C)
import qualified MagicHaskeller.Types as Types
import MagicHaskeller.TyConLib
import MagicHaskeller.PriorSubsts hiding (unify)

import MagicHaskeller.Analytical.Syntax
import MagicHaskeller.Analytical.Parser
import MagicHaskeller.Analytical.UniT
import MagicHaskeller.Analytical.FMExpr

-- | function specification by examples.
data Fun = BKF {maxNumBVs :: Int, arity :: Int, iopairs :: [IOPair], fmexpr :: FMExpr [IOPair]} -- ^ the function is really a background knowledge (and thus there is no need for loop check)     
         | Rec {maxNumBVs :: Int, arity :: Int, iopairs :: [IOPair], fmexpr :: FMExpr [IOPair], toBeSought :: TBS} -- ^ it is actually a recursive call.
mkBKFun          iops@(iop:_) = BKF {maxNumBVs = maximum $ map numUniIDs iops, arity = length $ inputs iop, iopairs = iops, fmexpr = iopsToFME iops}
mkRecFun ari tbs iops@(iop:_) = Rec {maxNumBVs = maximum $ map numUniIDs iops, arity = ari, iopairs = iops, fmexpr = iopsToVisFME tbs iops, toBeSought = tbs} -- arity = filter id tbs, but it is usually know beforehand
setIOPairs iops recfun@Rec{toBeSought=tbs} = recfun{iopairs = iops, fmexpr = iopsToVisFME tbs iops}
type BK  = [Types.Typed Fun]      -- ^ background knowledge.

applyFun s fun = fun{iopairs = applyIOPs s (iopairs fun)}


analyticSynth :: Search m => TyConLib -> VarLib -> [Dec] -> [Dec] -> m CoreExpr
analyticSynth tcl vl target bkdec = fst $ analyticSynthAndInfType tcl vl target bkdec
analyticSynthAndInfType :: Search m => TyConLib -> VarLib -> [Dec] -> [Dec] -> (m CoreExpr, Types.Type)
analyticSynthAndInfType tcl vl target bkdec
    = case unPS (liftM2 (,) (parseTypedIOPairss tcl xvl target) (parseTypedIOPairss tcl xvl bkdec)) Types.emptySubst 0 of
        Nothing -> error "Type error occurred while reading the IO pairs."
        Just (([],_),_,_) ->error "TypedIOPairs.analyticSynth*: No I/O pairs are defined as the target."
        Just (([(targetFunName, iops@(iop:_) Types.:::ty)],bktups),_,mx) ->
               let (bknames, bktiopss) = unzip bktups
                   (bkiopss, bktypes)  = unzipTyped bktiopss
                   target = mkRecFun aritar tbs iops
                   tbs = replicate aritar True                   
                   aritar = length $ inputs iop
                   bk = reverse $ zipWith (Types.:::) (map mkBKFun $ bkiopss) bktypes
               in (fmap (\e -> napply (length bktups) FunLambda $ napply aritar Lambda (Fix e aritar [aritar-1, aritar-2..0])) $   --  $ Fix e $ map X [arity-1, arity-2 .. 0]) $     -- 本当はこの結果のそれぞれに bknamesを適用したいのだが，bknamesなHValueがないので.... てゆーか，Expなら作れる．CoreExprも，BKが全部VarLibにはいっていれば作れる．
                   analSynthm {- analSynthNoUniT こっちだとちょっといい加減でちょっと速い -} bk (target Types.:::ty)
                  ,foldr (Types.:->) ty bktypes)
        _ -> error "TypedIOPairs.analyticSynth*: More than one I/O pairs are defined as the target."
    where xvl = mkXVarLib vl


analSynth, analSynthm :: Search m => BK -> Types.Typed Fun -> m CoreExpr
analSynth bk tfun@(fun Types.::: _) | any hasExistential $ map output $ iopairs fun = fmap fst $ runStateT (analSynthUTm bk tfun) emptySt
                                    | otherwise                                  = analSynthm bk tfun
analSynthm bk tfun
    = others `mplus` delay bkRelated
  where newbk = tfun : bk
        bkRelated = headSpine analSynth introBKm newbk tfun 
        others    = headSpine analSynthm (introConstr +++ introVarm +++ ndelayIntro 2 introCase) newbk tfun
headSpine rec intro bk tfun 
  = do (hd, subfuns, mbpivot) <- intro bk tfun
       subexps <- mapM (\subfun -> let arisub = arity $ Types.typee subfun in fmap (\e -> Fix e arisub (mkArgs mbpivot arisub)) $ rec bk subfun)  subfuns
       return (hd subexps)
#ifdef DEBUG
headSpine_debug rec trs intro bk tfun 
  = do (hd, subfuns, mbpivot) <- intro bk tfun
       subexps <- zipWithM (\tr subfun -> let arisub = arity $ Types.typee subfun in fmap (\e -> Fix e arisub (mkArgs mbpivot arisub)) $ rec tr bk subfun) trs subfuns
       return (hd subexps)
#endif

analSynthUTm :: Search m => BK -> Types.Typed Fun -> UniT m CoreExpr
-- analSynthm ([], _, _) = mzero -- If there is no example, nothing can be done. (But is this line necessary?)
analSynthUTm bk (fun Types.::: ty)
    = do 
         s <- gets subst
         let aptfun = applyFun s fun Types.::: ty
             newbk  = aptfun : bk
         headSpine analSynthUTm introAny newbk aptfun

mkArgs Nothing arisub = [arisub-1,arisub-2..0]
mkArgs (Just pivot) arisub = take pivot [arisub,arisub-1..] ++ [arisub-pivot-1,arisub-pivot-2..0]

#ifdef DEBUG
analyticSynthNoUniT_debug :: Search m => Tree (Introducer m) ->  TyConLib -> VarLib -> [Dec] -> [Dec] -> m CoreExpr
analyticSynthNoUniT_debug tree tcl vl target bkdec
    = case unPS (liftM2 (,) (parseTypedIOPairss tcl xvl target) (parseTypedIOPairss tcl xvl bkdec)) Types.emptySubst 0 of
        Nothing -> error "Type error occurred while reading the IO pairs."
        Just (([],_),_,_) ->error "TypedIOPairs.analyticSynth*: No I/O pairs are defined as the target."
        Just (([(targetFunName, iops@(iop:_) Types.:::ty)],bktups),_,mx) ->
               let (bknames, bktiopss) = unzip bktups
                   (bkiopss, bktypes)  = unzipTyped bktiopss
                   target = mkRecFun aritar tbs iops
                   tbs = replicate aritar True                   
                   aritar = length $ inputs iop
                   bk = reverse $ zipWith (Types.:::) (map mkBKFun $ bkiopss) bktypes
               in (fmap (\e -> napply (length bktups) FunLambda $ napply aritar Lambda (Fix e aritar [aritar-1, aritar-2..0])) $   --  $ Fix e $ map X [arity-1, arity-2 .. 0]) $     -- 本当はこの結果のそれぞれに bknamesを適用したいのだが，bknamesなHValueがないので.... てゆーか，Expなら作れる．CoreExprも，BKが全部VarLibにはいっていれば作れる．
                   analSynthNoUniT_debug tree bk (target Types.:::ty)
                  )
        _ -> error "TypedIOPairs.analyticSynth*: More than one I/O pairs are defined as the target."
    where xvl = mkXVarLib vl
-- analSynthNoUniT is inaccurate, but should work in most cases. This can be used in place of analSynthm
analSynthNoUniT bk tfun
  = headSpine analSynthNoUniT (introConstr +++ introVarm +++ ndelayIntro 2 introCase +++ ndelayIntro 1 introBKm) (tfun:bk) tfun
analSynthNoUniT_debug (Br intro trs) bk tfun
  = headSpine_debug analSynthNoUniT_debug trs intro (tfun:bk) tfun


data Tree x = Br x [Tree x] deriving Show
tryall, tryVar :: Search m => Tree (IntroUniT m)
tryall = Br introAny (repeat tryall)
tryVar = Br introVarUTm []
tryVarm :: (Functor m, MonadPlus m) => Tree (Introducer m)
tryVarm = Br introVarm []

analyticSynth_debug :: Search m => Tree (IntroUniT m) -> TyConLib -> VarLib -> [Dec] -> [Dec] -> m CoreExpr
analyticSynth_debug tree tcl vl target bkdec
    = do ((tgt,bktups),_,mx) <-
             unPS (liftM2 (,) (parseTypedIOPairss tcl xvl target) (parseTypedIOPairss tcl xvl bkdec)) Types.emptySubst 0
         case tgt of
           [] -> error "analyticSynth: No I/O pairs are defined as the target."
           [(targetFunName, iops@(iop:_) Types.:::ty)] ->
               let (bknames, bktiopss) = unzip bktups
                   (bkiopss, bktypes)  = unzipTyped bktiopss
                   target = mkRecFun aritar tbs iops
                   tbs = replicate aritar True
                   aritar = length $ inputs iop
                   bk = reverse $ zipWith (Types.:::) (map mkBKFun $ bkiopss) bktypes
               in fmap (\(e,_st) -> napply (length bktups) FunLambda $ napply aritar Lambda (Fix e aritar [aritar-1, aritar-2..0])) $   --  $ Fix e $ map X [arity-1, arity-2 .. 0]) $     -- 本当はこの結果のそれぞれに bknamesを適用したいのだが，bknamesなHValueがないので.... てゆーか，Expなら作れる．CoreExprも，BKが全部VarLibにはいっていれば作れる．
                        runStateT (analSynthUT_debug tree bk (target Types.::: ty)) emptySt -- ONLY DIFFER HERE.
           _ -> error "analyticSynth: More than one I/O pairs are defined as the target."
    where xvl = mkXVarLib vl

-- | 'analSynthUT_debug' can be used to try only the given introducer at each selection point. @analSynthUT = analSynthUT_debug tryall@
analSynthUT_debug :: Search m => Tree (IntroUniT m) -> BK -> Types.Typed Fun -> UniT m CoreExpr
analSynthUT_debug (Br intro iss) bk (fun Types.::: ty)
    = do
         s <- gets subst
         let aptfun = applyFun s fun Types.::: ty
             newbk   = aptfun : bk
         (hd, subfuns, mbpivot) <- intro newbk aptfun
         subexps <- zipWithM (\is subfun -> let arisub = arity $ Types.typee subfun in fmap (\e -> Fix e arisub (mkArgs mbpivot arisub)) $ analSynthUT_debug is newbk subfun) iss subfuns
         return (hd subexps)
#endif

type Introducer m = BK -> Types.Typed Fun -> m ([CoreExpr] -> CoreExpr, [Types.Typed Fun], Maybe Int)
type IntroUniT m = Introducer (UniT m)
-- NB: We should not use @StateT Env@ where @Env=(BK,TBS)@ because the Env affects only subexpressions.

il +++ ir = \bk iops -> il bk iops `mplus` ir bk iops
ndelayIntro n intro = \e a -> ndelay n $ intro e a

introAny :: Search m => IntroUniT m
introAny     =          introConstr +++ {- +/ -} (
                        introVarUTm +++
                        ndelayIntro 1 introBKUTm +++
                        ndelayIntro 2 introCase )

(+/) :: MonadPlus m => IntroUniT [] -> IntroUniT m -> IntroUniT m
m +/ n = \bk iops ->
         do st <- get
       	    case runStateT (m bk iops) st of [] -> n bk iops
                                             ts -> StateT $ \_ -> msum $ map return ts
liftList :: MonadPlus m => StateT s [] a -> StateT s m a
liftList = mapStateT (msum . map return)

introVarm, introConstr, introCase :: (Functor m, MonadPlus m) => Introducer m -- introConstrでは，実際にはCoreExprはConstrでよい．
introBKm :: (Search m) => Introducer m -- introConstrでは，実際にはCoreExprはConstrでよい．
introVarUTm, introBKUTm :: MonadPlus m => IntroUniT m
-- introVarUTmは一連のIgor関係の論文にはないものの，introBKが有効に働くには必要．これがないと，fを作るのにBKとしてfを使っても，introBKだけで終わってくれず，生成されない．
{- introBKのあとのintroVarUTmをintroBKに含めようとして，やっぱ止めた．
introVarUTm (iops,_,_,True) = mzero
introVarUTm (iops,_,_,False)  = msum $ map (\(ix,_) -> return (const (X ix), [])) $ filter (\(_,inp) -> inp == map output iops) $ zip [0..] $ transpose $ map inputs iops
-}
-- introVarUTm (iops,_,_,_)  = msum $ map (\(ix,_) -> return (const (X ix), [])) $ filter (\(_,inp) -> inp == map output iops) $ zip [0..] $ transpose $ map inputs iops
introVarUTm b f = liftList $ introVar (zipWithM_ appUnifyUT) b f
-- introVarm b f  = introVar (zipWithM_ unify) b f  *************************************** これはデバッグ時に有用なことも．
introVarm   = introVar (\a b -> guard $ a==b)

introVar :: MonadPlus m => ([Expr] -> [Expr] -> m ()) -> Introducer m
introVar cmp _ (fun Types.::: ty)
               = do let (argtys, retty) = Types.splitArgs ty
                        iops = iopairs fun
                        arifun = arity fun
                    let trins = transpose $ map inputs iops
--                    (ix,inps Types.::: argty) <- msum $ map return $ zip [0..] trins
                    (ix,argty,inps) <- msum [ return t | t@(_,aty,_) <- zip3 [0..] argtys $ visibles (toBeSought fun) trins, aty == retty ]

-- The following four lines should be equivalent to
                    cmp (map output iops) inps
-- but use of Maybe and simpler substitutions should be good for efficiency.
{-
                    st0 <- get
                    case runStateT (zipWithM_ appUnifyUT (map output iops) inps) st0{subst=[]} of
                              Just ((),st) -> put (st{subst= subst st `plusSubst` subst st0})
	  	     	      Nothing      -> mzero
-}
                    return (const (X ix), [], Nothing)

introConstr bk (fun Types.::: ty)
    = let argtys = Types.getArgs ty 
          iops   = iopairs fun
      in
      case [ output iop | iop <- iops ] of
        outs@(C _ (cid Types.::: cty) flds : rest)
            | all (`sConstrIs` cid) rest -> return (foldl (:$) (Primitive cid),
                                                    zipWith (\iops retty -> setIOPairs iops fun Types.::: Types.popArgs argtys retty)
                                                            (transpose [ divideIOP iop | iop <- iops ])
                                                            (case Types.revSplitArgs cty of (_,fieldtys,_) -> fieldtys),
                                                    Nothing)
        _                        -> mzero
divideIOP (IOP bvs ins out) = map (IOP bvs ins) $ fields out -- The actual number of buondVars may reduce, but not updating the field would not hurt.

shareConstr (C _ (cid Types.::: _) _ : iops) = all (`sConstrIs` cid) iops 
shareConstr _                = False
-- typeが違うと同じcidを異なるconstructorで使い得る場合，typeごと比較する必要があるが，現在はそうではないので．
C _ (c Types.::: _) _ `sConstrIs` cid = cid==c
_                     `sConstrIs` cid = False


select []     []      = []
select (b:bs) (p:ps) | b         = (p, False:bs) : rest
                     | otherwise = rest
            where rest = [ (result, b:newbs) | (result,newbs) <- select bs ps ]

introCase bk (fun Types.::: ty) = msum $ reverse $ zipWith introCase' [0..] $ select (toBeSought fun) trins
    where trins = transpose $ map inputs iops
          (argtys,retty) = Types.splitArgs ty
          iops   = iopairs fun
          arifun = arity fun
          introCase' :: MonadPlus m =>
                         Int              -- ^ the pivot position
                         -> ([Expr],TBS)  -- ^ (the pivot expression for each I/O pair, the next TBS)
                         -> m ([CoreExpr] -> CoreExpr, [Types.Typed Fun], Maybe Int)
          introCase' pos (pivots, tbs) -- includes variable cases. Overlapping patterns are not supported yet.
              = case mapM maybeCtor pivots of 
                  Nothing -> mzero
                  Just ctors
                      -> let
                             pipairs = zip pivots iops :: [(Expr,IOPair)]
                             ts      = IntMap.toList $ IntMap.fromListWith (\(t,xs) (_,ys) -> (t,xs++ys)) $
                                           zipWith (\(c Types.::: ct) x -> (c,(ct,[x]))) ctors pipairs
                                               :: [(Constr, (Types.Type, [(Expr,IOPair)]))]
                  -- Array.accum can also be used instead of IntMap because we can tell the range from that of VarLib.
                             hd ces = Case (X pos)
                                            (zipWith (\(constr, (_, (pivot,_):_)) ce -> (constr, length (fields pivot), ce))
                                                     ts
                                                     ces)
                             iopss  = [ Rec { maxNumBVs  = maxNumBVs fun, 
                                              arity      = arifun-1+lenflds,
                                              iopairs    = iops,
                                              fmexpr     = iopsToVisFME newtbs iops,
                                              toBeSought = newtbs
                                            }
                                          Types.::: Types.popArgs (dropNth pos argtys) (Types.popArgs (Types.getArgs cty) retty)
                                      | (_c, (cty, nextpipairs@((C _ _ flds', _):_))) <- ts,
                                        let lenflds = length flds'
                                            iops    = [ IOP bvs (reverse flds ++ is) o | (C _ _c flds, IOP bvs is o) <- nextpipairs ]
                                            newtbs  = replicate lenflds True ++ tbs
                                      ]
                         in return (hd, iopss, Just (arifun-pos-1))

dropNth pos bs = case splitAt pos bs of (tk,_:dr) -> tk ++ dr



introBKm   bk tfun = fromMx $ toMx $ introBK subtractIOPairsFromIOPairsBKm subtractIOPairsFromIOPairsm bk tfun
introBKUTm bk tfun = liftList $ introBK (const subtractIOPairsFromIOPairsBKUTm) (const subtractIOPairsFromIOPairsUTm) bk tfun
introBK subBK sub bk (fun Types.:::ty) = do          
                              let (argtys, retty) = Types.splitArgs ty
                              (ix, bkfun Types.::: bkty) <- msum $ map return $ tail $ zip [0..] bk
                                              -- The tail function here is used to avoid generating expressions like
                                              -- fix (\fa x1 ... xn -> fa ...).
                                              -- Such expressions would be excluded by the loop checker even without the tail function, 
                                              -- but we exclude them beforehand for efficiency reasons.
                              let (bkargtys, bkretty) = Types.splitArgs bkty
                              substy <- Types.match bkretty retty
                              iopss <- case bkfun of BKF{maxNumBVs=addendum} -> subBK (-addendum) (iopairs fun) bkfun
                                                     Rec{maxNumBVs=addendum} -> sub (-addendum) initTS (iopairs fun) bkfun
                              return (foldl (:$) (FunX ix),
                                      [ setIOPairs iops fun Types.::: tys 
                                      | iops Types.::: tys <- reverse $ 
                                                              zipWith (\x retty -> x Types.::: Types.popArgs argtys retty)
                                                                      (transpose iopss)
                                                                      (map (Types.apply substy) bkargtys) ],
                                      Nothing)



subtractIOPairsFromIOPairsBKUTm :: MonadPlus m => [IOPair] -> Fun -> UniT m [[IOPair]]
{-
subtractIOPairsFromIOPairsBK funs bks = foldr (liftM2 (:)) (return []) $ map (flip subtractIOPairs bks) funs
-}
subtractIOPairsFromIOPairsBKUTm []         bkf = return []
subtractIOPairsFromIOPairsBKUTm (fun:funs) bkf = do 
                                                 iops <- subtractIOPairsBKUTm fun bkf
                                                 iopss <- subtractIOPairsFromIOPairsBKUTm funs bkf -- iopsは，subfunctionが複数あって，それらsubfunctionsのそれぞれに関してIOPair1個ずつに過ぎない．
                                                 return (iops:iopss)
-- Applying substitutions to funs is not currently necessary (because funs does not include existential variables), but that will be useful in future versions which fill gaps of input examples.

{-
subtractIOPairs :: IOPair -> [IOPair] -> [[IOPair]] -- 内側リストの基数はfunのarity, 外側はbkのIO pairのうちmatchするのはいくつあるか
                                                    -- てゆーか，内側はbkのarityでは？
subtractIOPairs fun bkpairs = [ iops | bk <- bkpairs, iops <- subtractIOPair fun bk ]
-}
subtractIOPairsBKUTm :: MonadPlus m => IOPair -> Fun -> UniT m [IOPair] -- 内側リストの基数はfunのarity, 外側はbkのIO pairのうちmatchするのはいくつあるか
                                                    -- てゆーか，内側はbkのarityでは？
subtractIOPairsBKUTm tgt bkf = do 
                               s <- gets subst
                               let aptgt = applyIOP  s tgt
                                   apbkf = applyIOPs s $ iopairs bkf
                               bkiop <- msum $ map return apbkf
                               subtractIOPairUTm aptgt bkiop
subtractIOPairsFromIOPairsUTm :: MonadPlus m => TermStat -> [IOPair] -> Fun -> UniT m [[IOPair]]
{-
subtractIOPairsFromIOPairs funs bks = foldr (\fun rest -> do iops <- subtractIOPairs fun bks
                                                             iopss <- rest
                                                             return (iops:iopss)) (return []) funs
-}
subtractIOPairsFromIOPairsUTm ts []         bkf = return []
subtractIOPairsFromIOPairsUTm ts (fun:funs) bkf = do
                                (iops,newts) <- subtractIOPairsUTm ts fun bkf
                                iopss        <- subtractIOPairsFromIOPairsUTm newts funs bkf -- iopsは，subfunctionが複数あって，それらsubfunctionsのそれぞれに関してIOPair1個ずつに過ぎない．
                                return (iops:iopss)
subtractIOPairsUTm :: MonadPlus m => TermStat -> IOPair -> Fun -> UniT m ([IOPair], TermStat) -- 内側リストの基数はfunのarity, 外側はbkのIO pairのうちmatchするのはいくつあるか
                                                    -- てゆーか，内側はbkのarityでは？
subtractIOPairsUTm ts tgt bkf = do
                                s <- gets subst
                                let aptgt = applyIOP s tgt
                                    bktbs = toBeSought bkf
                                    -- apbkf = applyIOPs s bkf
                                    apvistgt = reverse $ visibles (reverse bktbs) $ reverse $ inputs aptgt
                                bkiop <- msum $ map return $ iopairs bkf
                                let visbki = visibles bktbs $ inputs bkiop
                                guard $ evalTS $ updateTS visbki apvistgt ts  -- applyするまえのbkfで投機的にfilterしてみたけど，いまいち．
                                let apvisbki = map (apply s) visbki
                                iops <- subtractIOPairUTm aptgt bkiop{inputs=apvisbki, output=apply s $ output bkiop}
                                let newts = updateTS apvisbki apvistgt ts -- This makes sure that the generated program does not go into a loop.
                                guard $ evalTS newts
--                                guard $ lessExprss (reverse bkis) (reverse apis)
                                return (iops, newts)

-- 例えば，join [x,y] [z,w] = [x,y,z,w]からbk [a,b] [c,d] = [a,c,b,d]を引くことを考える．
-- join [x,y] [z,w] = bk (f [x,y] [z,w]) (g [x,y] [z,w])においてf [x,y] [z,w] = [x,z], g [x,y] [z,w] = [y,w]なので，
-- subtractIOPair IOP{inputs=[[x,y],[z,w]],output=[x,y,z,w]} IOP{inputs=[[a,b],[c,d]],output=[a,c,b,d]} = [IOP{inputs=[[x,y],[z,w]],output=[x,z]}, IOP{inputs=[[x,y],[z,w]],output=[y,w]}]
-- ということになる．
subtractIOPairUTm :: MonadPlus m => IOPair -> IOPair -> UniT m [IOPair]
subtractIOPairUTm fun bkiop
                      = do frbkiop <- freshIOP bkiop
                           unifyUT (output frbkiop) (output fun)
                           s <- gets subst
                           return [ fun{output=apply s o} | o <- inputs frbkiop ] -- This @apply@ is necessary here because introBKm will soon forget the substitution.

subtractIOPairsFromIOPairsm :: Int -- maxNumBVsでゲットできる値．
                               -> TermStat -> [IOPair] -> Fun -> [] [[IOPair]]
subtractIOPairsFromIOPairsm addendum ts tgt bkf = subtractIOPairsFromIOPairsmFME addendum ts tgt bkf addendum
subtractIOPairsFromIOPairsmFME :: Int -> TermStat -> [IOPair] -> Fun -> Int -> [] [[IOPair]]
{-
subtractIOPairsFromIOPairsBK funs bks = foldr (liftM2 (:)) (return []) $ map (flip subtractIOPairs bks) funs
-}
subtractIOPairsFromIOPairsmFME addendum ts []         bkf offset = return []
subtractIOPairsFromIOPairsmFME addendum ts (fun:funs) bkf offset = do 
                                                 (iops,newts) <- subtractIOPairsmFME ts fun bkf offset
                                                 iopss <- subtractIOPairsFromIOPairsmFME addendum newts funs bkf (offset+addendum) -- iopsは，subfunctionが複数あって，それらsubfunctionsのそれぞれに関してIOPair1個ずつに過ぎない．
                                                 return (iops:iopss)
-- Applying substitutions to funs is not currently necessary (because funs does not include existential variables), but that will be useful in future versions which fill gaps of input examples.

subtractIOPairsmFME :: TermStat -> IOPair -> Fun -> Int -> [([IOPair], TermStat)] -- 返り値の[IOPair]は各引数に対応
subtractIOPairsmFME ts tgtiop bkf offset = do 
                                let vistgt = reverse $ visibles (reverse $ toBeSought bkf) $ reverse $ inputs tgtiop
                                visbkis <- unifyingIOPairs (output tgtiop) (fmexpr bkf) offset
                                let iops = [ tgtiop{output=o} | o <- visbkis ]
                                let newts = updateTS visbkis vistgt ts -- This makes sure that the generated program does not go into a loop.
                                guard $ evalTS newts
--                                guard $ lessExprss (reverse bkis) (reverse apis)
                                return (iops, newts)
                                
                                
subtractIOPairsFromIOPairsBKm :: Int -- maxNumBVsでゲットできる値．
                                 -> [IOPair] -> Fun -> [] [[IOPair]]
subtractIOPairsFromIOPairsBKm addendum tgt bkf = subtractIOPairsFromIOPairsBKmFME addendum tgt (iopsToFME $ iopairs bkf) addendum
subtractIOPairsFromIOPairsBKmFME :: Int -> [IOPair] -> FMExpr [IOPair] -> Int -> [] [[IOPair]]
{-
subtractIOPairsFromIOPairsBK funs bks = foldr (liftM2 (:)) (return []) $ map (flip subtractIOPairs bks) funs
-}
subtractIOPairsFromIOPairsBKmFME addendum []         bkf offset = return []
subtractIOPairsFromIOPairsBKmFME addendum (fun:funs) bkf offset = do 
                                                 iops <- subtractIOPairsBKmFME fun bkf offset
                                                 iopss <- subtractIOPairsFromIOPairsBKmFME addendum funs bkf (offset+addendum) -- iopsは，subfunctionが複数あって，それらsubfunctionsのそれぞれに関してIOPair1個ずつに過ぎない．
                                                 return (iops:iopss)
-- Applying substitutions to funs is not currently necessary (because funs does not include existential variables), but that will be useful in future versions which fill gaps of input examples.
                              
                                
                                
subtractIOPairsBKmFME :: IOPair -> FMExpr [IOPair] -> Int -> [] [IOPair] -- 返り値の[IOPair]は各引数に対応
subtractIOPairsBKmFME tgtiop bkfme offset = do 
                                visbkis <- unifyingIOPairs (output tgtiop) bkfme offset
                                return [ tgtiop{output=o} | o <- visbkis ]


unifyingIOPairs :: Expr -> FMExpr [IOPair] -> Int -> [] [Expr]
unifyingIOPairs e fme 0      = [ inputs iop | (iops, _) <- unifyFME e fme, iop <- iops ]
unifyingIOPairs e fme offset = [ map (mapE (offset+) . apfresh s) $ inputs iop | (iops, s) <- unifyFME e fme, iop <- iops ]
