{-#LANGUAGE GADTs, DeriveDataTypeable, DeriveFunctor #-}
module Control.Search
(
search, sat, ctrex, searchRaw, usearch
, test, testTime
, Options (..)
, sat', search', ctrex', searchRaw', test', testTime'
, (&&&), (|||), (==>), nott, true, false
, Cool(..)
, Coolean
, module Control.Enumerable
) where
import Data.IORef
import Control.Sized
import Control.Enumerable
import System.IO.Unsafe
import Control.Enumerable.Count
import Data.Coolean
import System.Timeout
newCounter :: IO (IO Int)
newCounter = do
ref <- newIORef 0
return (atomicModifyIORef ref (\i -> (i+1,i)))
attach :: (IO Int) -> a -> IO (IO Int, a)
attach c a = do
ref <- newIORef (-1)
return (readIORef ref, unsafePerformIO $ c >>= writeIORef ref >> return a)
data Value a where
Pair :: (a,b) -> Value a -> Value b -> Value (a,b)
Map :: a -> (b -> a) -> Value b -> Value a
Unit :: a -> Value a
Alt :: a -> Value a -> Minimal a -> Value a
instance Show a => Show (Value a) where
show v = "("++ repV v ++ ", " ++ show (plainV v) ++ ")"
instance Functor Value where
fmap f a = Map (f (plainV a)) f a
repV :: Value a -> String
repV (Unit _) = "1"
repV (Pair _ a b) = "("++ repV a ++ ", " ++ repV b ++ ")"
repV (Alt _ a _) = "?"++ repV a
repV (Map a f v) = "$"++ repV v
plainV :: Value a -> a
plainV (Pair a _ _) = a
plainV (Map a _ _) = a
plainV (Unit a) = a
plainV (Alt a _ _) = a
mkPair :: Value a -> Value b -> Value (a,b)
mkPair (Unit a) (Unit b) = Unit (a,b)
mkPair (Unit a) v = mkMap ((,) a) v
mkPair v (Unit b) = mkMap (\a -> (a,b)) v
mkPair v1 v2 = Pair (plainV v1,plainV v2) v1 v2
mkAlt :: Value a -> Minimal a -> Value a
mkAlt v s = Alt (plainV v) v s
mkMap :: (a -> b) -> Value a -> Value b
mkMap f (Unit a) = Unit (f a)
mkMap f (Map a g v) = Map (f a) (f . g) v
mkMap f v = Map (f (plainV v)) f v
data Minimal a where
Pay :: Minimal a -> Minimal a
Value :: Value a -> Minimal a
Empty :: Minimal a
deriving Typeable
instance Functor Minimal where
fmap f (Pay s) = Pay (fmap f s)
fmap f (Value v) = Value (fmap f v)
fmap _ Empty = Empty
instance Applicative Minimal where
pure a = Value (Unit a)
sf <*> sa = fmap (uncurry ($)) (pair sf sa)
instance Alternative Minimal where
empty = Empty
Empty <|> s = s
s <|> Empty = s
Pay a <|> Pay b = Pay (a <|> b)
Value vf <|> s = Value (mkAlt vf s)
a <|> Value vf = Value (mkAlt vf a)
instance Sized Minimal where
pay = Pay
pair Empty _ = Empty
pair _ Empty = Empty
pair (Pay a) b = Pay (pair a b)
pair a (Pay b) = Pay (pair a b)
pair (Value f) (Value g) = Value (mkPair f g)
naturals = pure 0 <|> pay (fmap (+1) naturals)
aconcat [] = empty
aconcat [s] = s
aconcat [s1,s2] = s1 <|> s2
aconcat ss0 = case extr ss0 of
([],m) -> maybe Empty Value m
(ss',m) -> maybe (pay (aconcat ss')) (Value . (`mkAlt` (aconcat ss'))) m
where
extr :: [Minimal a] -> ([Minimal a], Maybe (Value a))
extr (s:ss) = case s of
Value v -> (ss,Just v)
Empty -> extr ss
Pay s' -> case extr ss of
(ss',Nothing) -> (s':ss',Nothing)
(ss',j) -> (s:ss', j)
extr _ = ([], Nothing)
data Observed a = Observed { sizeLeft :: Int, val :: Value a } deriving Functor
instance Show a => Show (Observed a) where
show = show . val
minimal :: Minimal a -> Int -> Maybe (Observed a)
minimal _ n | n < 0 = Nothing
minimal Empty _ = Nothing
minimal (Pay s) n = minimal s (n-1)
minimal (Value vf) n = Just (Observed n vf)
data Relevant a = Relevant
{
evalOrder :: Int,
fixed :: Value a,
swapped :: Observed a
}
(<<) :: Relevant a -> Relevant b -> Bool
r << q = evalOrder r < evalOrder q
merge :: Value a -> Value b -> [Relevant a] -> [Relevant b] -> [Relevant (a,b)]
merge va _ [] r = rsmap (mkPair va) r
merge _ vb r [] = rsmap (`mkPair` vb) r
merge va vb rs@(r:rs') qs@(q:qs')
| q << r = rmap (va `mkPair`) q : merge va (fixed q) rs qs'
| otherwise = rmap (`mkPair` vb) r : merge (fixed r) vb rs' qs
rmap :: (Value a -> Value b) -> Relevant a -> Relevant b
rmap f r = r{fixed = f (fixed r), swapped = omap f (swapped r)} where
omap g o' = o'{val = g (val o')}
rsmap :: (Value a -> Value b) -> [Relevant a] -> [Relevant b]
rsmap f = map (rmap f)
observed :: Observed a -> IO (IO [Observed a], a)
observed o = do
c <- newCounter
let go :: Value a -> IO (IO [Relevant a], a)
go (Pair _ va vb) = do
(rs, xa) <- go va
(qs, xb) <- go vb
return (liftA2 (merge va vb) rs qs, (xa,xb))
go (Map _ f v) = do
~(x,a) <- (go v)
return (fmap (rsmap (fmap f)) x,f a)
go (Unit a) = return (return [], a)
go (Alt _ v x) = do
~(tr,a) <- go v
(i, a') <- attach c a
return (tralt tr i, a')
where
tralt tr i = i >>= \n -> case n of
-1 -> return []
_ -> case minimal x (sizeLeft o) of
Just nv -> fmap (Relevant n v nv :) tr
Nothing -> tr
fmap (\(a,b) -> (fmap fx a, b)) $ go (val o) where
fx :: [Relevant a] -> [Observed a]
fx rs = reverse (map swapped rs)
observedc :: Observed a -> IO (IO Int, IO [Observed a], a)
observedc o = do
ref <- newIORef 0
let c = atomicModifyIORef ref (\i -> (i+1,i))
let go :: Value a -> IO (IO [Relevant a], a)
go (Pair _ va vb) = do
(rs, xa) <- go va
(qs, xb) <- go vb
return (liftA2 (merge va vb) rs qs, (xa,xb))
go (Map _ f v) = do
~(x,a) <- (go v)
return (fmap (rsmap (fmap f)) x,f a)
go (Unit a) = return (return [], a)
go (Alt _ v x) = do
~(tr,a) <- go v
(i, a') <- attach c a
return (tralt tr i, a')
where
tralt tr i = i >>= \n -> case n of
-1 -> return []
_ -> case minimal x (sizeLeft o) of
Just nv -> fmap (Relevant n v nv :) tr
Nothing -> tr
fmap (\(a,b) -> (readIORef ref, fmap fx a, b)) $ go (val o) where
fx :: [Relevant a] -> [Observed a]
fx rs = reverse (map swapped rs)
type State a = [[Observed a]]
type StateP a = [(Sched, [Observed a])]
{-#INLINE stepD #-}
stepD :: (a -> Bool) -> State a -> IO ((Bool, a), State a)
stepD q ((o:os):s) = do
let s' = if null os then s else os:s
(ins, a) <- observed o
let b = q a
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else os':s')
stepD _ _ = error $ "Invalid state"
searchRawD :: Enumerable a => Int -> (a -> Bool) -> IO [(Bool,a)]
searchRawD n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy q [[o]]) mo
where
lazy :: (a -> Bool) -> State a -> IO [(Bool,a)]
lazy _ [] = return []
lazy q' s = do
(ba,s') <- stepD q' s
fmap (ba:) (unsafeInterleaveIO $ lazy q' s')
stepF :: (a -> Cool) -> StateP a -> IO ((Bool, a), StateP a)
stepF q ((sc,(o:os)):s) = do
let s' = if null os then s else (sc,os):s
(ins, a) <- observed o
let (sc',b) = par sc (q a)
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else ((sc',os'):s'))
stepF _ _ = error $ "Invalid state"
searchRawF :: Enumerable a => Int -> (a -> Cool) -> IO [(Bool,a)]
searchRawF n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy q [(sched0, [o])]) mo
where
lazy :: (a -> Cool) -> StateP a -> IO [(Bool,a)]
lazy _ [] = return []
lazy q' s = do
(ba,s') <- stepF q' s
fmap (ba:) (unsafeInterleaveIO $ lazy q' s')
stepO :: (a -> Cool) -> StateP a -> IO ((Bool, a), StateP a)
stepO q ((sc,(o:os)):s) = do
let s' = if null os then s else (sc,os):s
let (sc', _) = lookahead sc (q (plainV (val o)))
(ins, a) <- observed o
let b = run sc' (q a)
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else ((sc',os'):s'))
stepO _ _ = error $ "Invalid state"
searchRawO :: Enumerable a => Int -> (a -> Cool) -> IO [(Bool,a)]
searchRawO n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy [(sched0, [o])]) mo
where
lazy [] = return []
lazy s = do
(ba,s') <- stepO q s
fmap (ba:) (unsafeInterleaveIO $ lazy s')
stepOF :: (a -> Cool) -> StateP a -> IO ((Bool, a), StateP a)
stepOF q ((sc,(o:os)):s) = do
let s' = if null os then s else (sc,os):s
let (sc', _) = lookahead sc (q (plainV (val o)))
(ins, a) <- observed o
let (sc'',b) = par sc' (q a)
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else ((sc'',os'):s'))
stepOF _ _ = error $ "Invalid state"
searchRawOF :: Enumerable a => Int -> (a -> Cool) -> IO [(Bool,a)]
searchRawOF n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy [(sched0, [o])]) mo
where
lazy [] = return []
lazy s = do
(ba,s') <- stepOF q s
fmap (ba:) (unsafeInterleaveIO $ lazy s')
stepOS :: (a -> Cool) -> StateP a -> IO ((Bool, a), StateP a)
stepOS q ((sc,(o:os)):s) = do
let s' = if null os then s else (sc,os):s
(c,_, a1) <- observedc o
(sc', _) <- subsetsc c sc (q a1)
(ins, a) <- observed o
let b = run sc' (q a)
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else ((sc',os'):s'))
stepOS _ _ = error $ "Invalid state"
searchRawOS :: Enumerable a => Int -> (a -> Cool) -> IO [(Bool,a)]
searchRawOS n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy [(sched0, [o])]) mo
where
lazy [] = return []
lazy s = do
(ba,s') <- stepOS q s
fmap (ba:) (unsafeInterleaveIO $ lazy s')
stepOSF :: (a -> Cool) -> StateP a -> IO ((Bool, a), StateP a)
stepOSF q ((sc,(o:os)):s) = do
let s' = if null os then s else (sc,os):s
(c,_, a1) <- observedc o
(sc', _) <- subsetsc c sc (q a1)
(ins, a) <- observed o
let (sc'',b) = par sc' (q a)
() <- b `seq` return ()
os' <- ins
return ((b, plainV (val o)), if null os' then s' else ((sc'',os'):s'))
stepOSF _ _ = error $ "Invalid state"
searchRawOSF :: Enumerable a => Int -> (a -> Cool) -> IO [(Bool,a)]
searchRawOSF n q = do
let mo = minimal local n
maybe (return []) (\o -> lazy [(sched0, [o])]) mo
where
lazy [] = return []
lazy s = do
(ba,s') <- stepOSF q s
fmap (ba:) (unsafeInterleaveIO $ lazy s')
data Options
=
D
| O
| F
| OF
| OS
| OSF
deriving (Show, Read, Eq, Enum)
defOptions :: Coolean cool => (a -> cool) -> Options
defOptions p | isCool p = OF
defOptions _ = D
searchRaw :: (Enumerable a, Coolean cool) => Int -> (a -> cool) -> IO [(Bool,a)]
searchRaw n p = searchRaw' (defOptions p) n p
searchRaw' :: (Enumerable a, Coolean cool) => Options -> Int -> (a -> cool) -> IO [(Bool,a)]
searchRaw' D n p = searchRawD n (toBool . p)
searchRaw' O n p = searchRawO n (toCool . p)
searchRaw' F n p = searchRawF n (toCool . p)
searchRaw' OF n p = searchRawOF n (toCool . p)
searchRaw' OS n p = searchRawOS n (toCool . p)
searchRaw' OSF n p = searchRawOSF n (toCool . p)
search :: (Enumerable a, Coolean cool) => Int -> (a -> cool) -> IO [a]
search n p = search' (defOptions p) n p
search' :: (Enumerable a, Coolean cool) => Options -> Int -> (a -> cool) -> IO [a]
search' o n q = do
xs <- searchRaw' o n q
return [a | (b,a) <- xs, b]
sat :: (Enumerable a, Coolean cool) => Int -> (a -> cool) -> Bool
sat n p = sat' (defOptions p) n p
sat' :: (Enumerable a, Coolean cool) => Options -> Int -> (a -> cool) -> Bool
sat' o n q = unsafePerformIO (fmap (not . null) (search' o n q))
usearch :: Enumerable a => Int -> (a -> Bool) -> [a]
usearch n p = usearch' (defOptions p) n p
usearch' :: Enumerable a => Options -> Int -> (a -> Bool) -> [a]
usearch' o n q = unsafePerformIO (search' o n q)
ctrex :: (Coolean cool, Enumerable a) => Int -> (a -> cool) -> IO (Either Integer a)
ctrex n p = ctrex' (defOptions p) n p
ctrex' :: (Coolean cool, Enumerable a) => Options -> Int -> (a -> cool) -> IO (Either Integer a)
ctrex' o n0 q0 = do
let q = nott . q0
xs <- searchRaw' o n0 q
return (go xs 0)
where go [] n = Left n
go ((b,a):_) _ | b = Right a
go (_ :xs') n = go xs' $! (n+1)
countdom :: Enumerable a => (a -> b) -> Count a
countdom _ = global
test' :: (Coolean cool, Enumerable a, Show a) => Options -> (a -> cool) -> IO ()
test' o q = go 0 0 (count (countdom q)) where
go _ _ [] = putStrLn "No counterexample found"
go n acc (c:cs) = do
let acc' = acc + c
putStrLn $ "Testing to size "++ show n ++ ", worst case "++show acc'++" tests"
e <- ctrex' o n q
case e of
Left t -> putStrLn ("No counterexample found in "++show t++" tests") >> putStrLn "" >>
go (n+1) acc' cs
Right x -> putStrLn "Counterexample found:" >> print x
test :: (Coolean cool, Enumerable a, Show a) => (a -> cool) -> IO ()
test p = test' (defOptions p) p
testTime :: (Coolean cool, Enumerable a, Show a) => Int -> (a -> cool) -> IO ()
testTime t p = testTime' (defOptions p) t p
testTime' :: (Coolean cool, Enumerable a, Show a) => Options -> Int -> (a -> cool) -> IO ()
testTime' o t p = do
mu <- timeout (t*1000000) (test' o p)
case mu of Nothing -> putStrLn "Timed out"
Just{} -> return ()