packages feed

lapack-0.4: test/Test/Logic.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE Rank2Types #-}
module Test.Logic where

import Test.Utility (Match(Match,Mismatch))

import qualified UniqueLogic.ST.TF.Rule as Rule
import qualified UniqueLogic.ST.TF.System.Simple as Sys
import qualified UniqueLogic.ST.TF.System as Sys (runApplyM)

import qualified Data.Ref as Ref
import Data.STRef (newSTRef, writeSTRef, readSTRef)

import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Shape ((::+)((::+)))

import qualified Control.Monad.Trans.Class as MT
import qualified Control.Monad.Trans.RWS as MRWS
import Control.Monad.ST (ST, runST)
import Control.Applicative (Applicative, liftA2, pure, (<$>))

import qualified Data.Monoid.Applicative as AppMn
import Data.Semigroup ((<>))

import qualified Test.QuickCheck as QC
import qualified Test.QuickCheck.GenT as GenT
import Test.QuickCheck.Gen (Gen(MkGen))
import Test.QuickCheck.GenT (GenT)

import System.Random (Random)


data MatchMode = DontForceMatch | ForceMatch
   deriving (Eq, Show)

newtype M s a = M {runM :: MRWS.RWST (Int, MatchMode) Match () (GenT (ST s)) a}
   deriving (Functor, Applicative, Monad)

instance Ref.C (M s) where
   new =
      Ref.newCons
         (liftST . newSTRef)
         ((liftST .) . writeSTRef)
         (liftST . readSTRef)

liftST :: ST s a -> M s a
liftST = M . MT.lift . MT.lift

liftGen :: QC.Gen a -> M s a
liftGen = M . MT.lift . GenT.liftGen


type Variable s a = Sys.Variable (M s) a
type System s = AppMn.T (Sys.T (M s)) ()


type ShapeInt = Shape.ZeroBased Int

example :: Int -> MatchMode -> QC.Gen ([ShapeInt], Match)
example =
   runSTInGen (do
      a <- Sys.globalVariable
      b <- Sys.globalVariable
      c <- Sys.globalVariable
      d <- Sys.globalVariable
      e <- Sys.globalVariable
      f <- Sys.globalVariable
      solve $  a<!=b  <>  d=!=f  <>  c=!=d
      mapM query [a,b,c,d,e,f])


solve :: System s -> M s ()
solve = Sys.solve . AppMn.run

choose :: (Random a) => (Int -> (a,a)) -> M s a
choose f = liftGen . QC.choose . f . fst =<< M MRWS.ask

liftZeroBased ::
   (Functor f) =>
   (a -> f b) -> Shape.ZeroBased a -> f (Shape.ZeroBased b)
liftZeroBased f (Shape.ZeroBased x) = Shape.ZeroBased <$> f x

class (Dim dim) => DimInclZero dim where
   (<!=) :: Variable s dim -> Variable s ShapeInt -> System s

instance (n ~ Int) => DimInclZero (Shape.ZeroBased n) where
   va <!= vb  =  AppMn.Cons $ do
      assignmentM (liftZeroBased $ \a -> choose (\ maxk -> (a,maxk))) va vb
      assignmentM (liftZeroBased $ \b -> choose (\_maxk -> (0,b))) vb va

instance DimInclZero Shape.Zero where
   va <!= vb  =  AppMn.Cons $ do
      assignmentM
         (\Shape.Zero -> Shape.ZeroBased <$> choose (\maxk -> (0,maxk)))
         va vb
      Sys.runApply (pure Shape.Zero) va

{-
We cannot split this into something like

a <!= c <> c <!= a+b

because the solver might choose in this order

a+b=6::+4
c=5

and then it is left with a>c from which it cannot recover.
-}
between :: Variable s (ShapeInt::+ShapeInt) -> Variable s ShapeInt -> System s
between vab vc  =  AppMn.Cons $ do
   assignmentM
      (\(Shape.ZeroBased a ::+ Shape.ZeroBased b) ->
         Shape.ZeroBased <$> choose (\_maxk -> (a,a+b)))
      vab vc
   assignmentM
      (\(Shape.ZeroBased c) ->
         liftA2 (::+)
            (Shape.ZeroBased <$> choose (\_maxk -> (0,c)))
            (Shape.ZeroBased <$> choose (\ maxk -> (c,maxk))))
      vc vab


class (Shape.C dim) => Dim dim where chooseDim :: M s dim
instance (i ~ Int) => Dim (Shape.ZeroBased i) where
   chooseDim = fmap Shape.ZeroBased $ choose ((,) 0)
instance Dim Shape.Zero where chooseDim = return Shape.Zero
instance Dim () where chooseDim = return ()
instance (Dim dimA, Dim dimB) => Dim (dimA,dimB) where
   chooseDim = liftA2 (,) chooseDim chooseDim
instance (Dim dimA, Dim dimB) => Dim (dimA::+dimB) where
   chooseDim = liftA2 (::+) chooseDim chooseDim

(=!=) :: (Dim dim, Eq dim) => Variable s dim -> Variable s dim -> System s
va =!= vb  =  AppMn.Cons $ do
   let equalM =
         assignmentM $ \x -> do
            matchMode <- M $ MRWS.asks snd
            case matchMode of
               ForceMatch -> return x
               DontForceMatch -> do
                  y <- chooseDim
                  M $ MRWS.tell $ if x==y then Match else Mismatch
                  return y
   equalM va vb
   equalM vb va

assignmentM ::
   (Ref.C s) => (a -> s b) -> Sys.Variable s a -> Sys.Variable s b -> Sys.T s ()
assignmentM f vx vy = Sys.runApplyM (f <$> Sys.arg vx) vy


(!+!) ::
   Variable s dimA -> Variable s dimB -> Variable s (dimA::+dimB) -> System s
(!+!) va vb vab = AppMn.Cons $ do
   Sys.assignment3 (::+) va vb vab
   Sys.assignment2 (\(a::+_) -> a) vab va
   Sys.assignment2 (\(_::+b) -> b) vab vb

(!*!) ::
   Variable s dimA -> Variable s dimB -> Variable s (dimA,dimB) -> System s
(!*!) va vb vab = AppMn.Cons $ Rule.pair va vb vab


runSTInGen :: (forall s. M s b) -> Int -> MatchMode -> QC.Gen (b, Match)
runSTInGen m =
   \maxDim matchMode -> MkGen $ \r n ->
      runST (GenT.unGenT (MRWS.evalRWST (runM m) (maxDim,matchMode) ()) r n)

query :: (Dim dim) => Variable s dim -> M s dim
query v = do
   mk <- Sys.query v
   case mk of
      Just k -> return k
      Nothing -> do
         k <- chooseDim
         Sys.solve $ Rule.equ v =<< Sys.constant k
         return k