packages feed

every-bit-counts-0.1: PBasicGames.hs

{-# options_ghc -XEmptyDataDecls -XOverlappingInstances -XScopedTypeVariables -XGADTs #-}
module PBasicGames where 

import Data.Maybe
import Iso
import PGames
import List

type Nat = Int 

-- Game for () 

-- /boolGame/
unitGame :: Game () 
unitGame = Single (Iso (\() -> ()) (\() -> ()))

boolIso :: ISO Bool (Either () ())
boolIso = 
  Iso (\b -> if b then Left() else Right())
      (\x -> case x of Left() -> True; Right() -> False)

boolGame :: Game Bool
boolGame = split boolIso unitGame unitGame
-- /End/


-- /constGame/ 
constGame :: t -> Game t
constGame k = Single (Iso (const ()) (const k))
-- /End/


-- Games for natural numbers
parityIso :: ISO Int (Either Int Int)
parityIso = Iso 
  (\n -> if even n then Left (n `div` 2) else Right (n `div` 2)) 
  (\x -> case x of Left m -> m*2; Right m -> m*2+1)


-- /geNatGame/
-- geNatGame k returns a game for { n:Nat | n >= k }
geNatGame :: Nat -> Game Nat 
geNatGame k = split iso (constGame k) (geNatGame (k+1)) 
  where iso :: ISO Nat (Either Nat Nat) 
        iso = Iso ask bld 
        -- Precondition of ask x: x >= k
        ask x = if x == k then Left x else Right x
        bld (Left x)  = x 
        bld (Right x) = x 
-- /End/

-- /unaryNatGame/ 
succIso :: ISO Nat (Either () Nat)
succIso = Iso ask bld
  where ask 0         = Left ()
        ask (n+1)     = Right n
        bld (Left ()) = 0 
        bld (Right n) = n+1

unaryNatGame :: Game Nat 
unaryNatGame = split succIso unitGame unaryNatGame
-- /End/ 

-- /encUnaryNat/
encUnaryNat x = case x of 0 -> 1 : []
                          n+1 -> 0 : encUnaryNat n
-- /End/


-- /binNatGame/
binNatGame :: Game Nat
binNatGame = split succIso unitGame divG
 where divG = split (Iso ask bld) binNatGame binNatGame
       ask n | even n    = Left (n `div` 2)
             | otherwise = Right (n `div` 2)
       bld (Left m)      = 2*m 
       bld (Right m)     = 2*m+1 
-- /End/ 

-- /natGameFunny/ 
natGameFunny :: Game Nat 
natGameFunny = split (Iso ask bld) (constGame 0) gfunny
 where ask n = if n == 0 then Left n else Right n                                   -- Are you 0 or not? 
       bld (Left n)  = n 
       bld (Right n) = n 

       gfunny = split evi binNatGame (split pred_evi binNatGame voidNatGame)
       evi      = Iso (\n -> if even n then Left (n `div` 2) else Right n)         -- Are you even or not?
                      (either (\n->2*n) id)
       pred_evi = Iso (\(n+1) -> if even n then Left (n `div` 2) else Right (n+1)) -- Is your predecessor odd or not?
                      (either (\n->2*n+1) id)
    
       voidNatGame :: Game Nat 
       -- In reality: Game { x: Nat | x > 0 /\ x odd /\ x-1 odd }
       voidNatGame = split voidi voidNatGame voidNatGame 
       -- Empty set is disjoint with any set so voidi *is* an isomorphism
       voidi = Iso (\x -> Right x) bld 
-- /End/

binNatGameFunny :: Game Nat
binNatGameFunny = split (Iso ask bld) zOneG divG
  where zOneG = split (Iso askz bldz) (constGame 0) 
                                      (constGame 1)
        askz 0 = Left 0 
        askz 1 = Right 1 
        bldz (Right 1) = 1 
        bldz (Left 0)  = 0 
        -- the rest as in binNatGame 
-- /End/ 
        ask 0 = Left 0
        ask (n+1) = Right n 
        bld (Left 0) = 0 
        bld (Right n) = n+1 

        divG = split iso binNatGame binNatGame
        iso = Iso ask' bld' 
        ask' n = if even n then Left (n `div` 2)
                 else Right (n `div` 2)
        bld' (Left m)  = 2*m 
        bld' (Right m) = 2*m+1 



-- Flip the meaning of the bits
{-
flipGame :: Game a -> Game a
flipGame (Split iso f1 g1 f0 g0) = Split (iso `seqI` swapSumI) f0 (flipGame g0) f1 (flipGame g1)
flipGame g = g
-}

-- A game for sums 
-- /sumGame/
sumGame :: Game t -> Game s -> Game (Either t s)
sumGame = split idI
-- /End/


-- A game for products, based on appending
-- /prodGame/ 
data ProdGamesResult s t where
  PGR :: ISO (s,t) s' -> GamesOver s' -> ProdGamesResult s t
  
prodGame :: forall t s. Game t -> Game s -> Game (t,s)
prodGame (Single iso) g = g +> iso'
  where iso' :: ISO (t,s) s -- assuming ISO t ()
        iso' = prodI iso idI `seqI` prodLUnitI
prodGame (Split (Iso i j) gs) g = 
  case prodGames gs of
    PGR (Iso i' j') gs' ->
      Split (Iso (\(x,y) -> i' (i x, y)) (\z -> let (x,y) = j' z in (j x,y))) gs'
  where 
    prodGames :: forall sum. GamesOver sum -> ProdGamesResult sum s
    prodGames gs =
      case gs of
        NilGames -> PGR (Iso (\_ -> error "should not happen") (\_ -> error "should not happen")) NilGames
        ConsGames w ga gsa ->
          case prodGames gsa of
            PGR (Iso i j) gs'' -> PGR (Iso (\(x,y) -> case x of Left xl -> Left (xl,y); Right xr -> Right (i (xr,y))) 
                                           (\x -> case x of Left (y,z) -> (Left y,z); Right z -> let (z1,z2) = j z in (Right z1,z2))) (ConsGames w (prodGame ga g) gs'')


-- /End/

-- A game for products, based on interleaving
-- /ilGame/
        {-
ilGame :: forall t s. Game t -> Game s -> Game (t,s)
ilGame (Single iso) g2 = g2 +> iso' 
  where iso' :: ISO (t,s) s -- assuming ISO t ()
        iso' = prodI iso idI `seqI` prodLUnitI
ilGame (Split (iso :: ISO t (Either ta tb)) f1a g1a f1b g1b) g2 
  = Split iso' f1a (ilGame g2 g1a) f1b (ilGame g2 g1b) 
  where iso' :: ISO (t,s) (Either (s,ta) (s,tb))
        iso' =  swapProdI `seqI` prodI idI iso 
                          `seqI` prodRSumI 
-}
-- /End/


-- /depGame/

depGame :: forall t s. Game t -> (t -> Game s) -> Game (t,s)
depGame (Single iso) f = f (from iso ()) +> iso'
  where iso' = prodI iso idI `seqI` prodLUnitI
depGame (Split (Iso i j) gs) f = 
  case depGames gs (f . j) of
    PGR (Iso i' j') gs' -> Split (Iso (\(x,y) -> i' (i x, y)) (\z -> let (x,y) = j' z in (j x,y))) gs'
  where
    depGames :: forall sum. GamesOver sum -> (sum -> Game s) -> ProdGamesResult sum s
    depGames gs f = 
      case gs of
        NilGames -> PGR (Iso (\_ -> error "should not happen") (\_ -> error "should not happen")) NilGames
        ConsGames w ga gsa ->
          case depGames gsa (f . Right) of
            PGR (Iso i'' j'') gs'' -> 
              PGR (Iso (\(x,y) -> case x of Left xl -> Left (xl,y); Right xr -> Right (i'' (xr,y))) 
                       (\x -> case x of Left (y,z) -> (Left y,z); Right z -> let (z1,z2) = j'' z in (Right z1,z2))) 
                  (ConsGames w (depGame ga (f . Left)) gs'')

-- /End/





getRight (Right x) = x 
getLeft (Left x)   = x 


nonemptyIso = Iso (\(x:xs) -> (x,xs)) (\(x,xs) -> x:xs) 



-- /vecGame/ 
vecGame :: Game t -> Nat -> Game [t]
-- /End/
vecGame g 0 = constGame []
vecGame g (n+1) = prodGame g (vecGame g n) +> nonemptyIso 

-- /lengthListGame/
lengthListGame :: Game t -> Game (Nat,[t]) 
lengthListGame g = depGame binNatGame (vecGame g) 

listGame' :: forall t. Game t -> Game [t] 
listGame' g = lengthListGame g +> Iso h j 
  where h :: [t] -> (Nat,[t]) 
        h lst = (length lst, lst) 
        j :: (Nat,[t]) -> [t] 
        -- Precondition: n = length lst
        j (n,lst) = lst 
-- /End/ 



-- A game for lists, using sum-of-products
-- /listGame/ 
listIso :: ISO [t] (Either () (t,[t]))
listIso = Iso ask bld
  where ask []             = Left () 
        ask (x:xs)         = Right (x,xs) 
        bld (Left ())      = [] 
        bld (Right (x,xs)) = x:xs 

listGame :: Game t -> Game [t]
listGame g = 
  split listIso unitGame (prodGame g (listGame g))
  
-- Parameterized on how much more likely a Cons is than a Nil
biasedListGame :: Int -> Game t -> Game [t]
biasedListGame n g = 
  split2 listIso 1 unitGame n (prodGame g (biasedListGame n g))
-- /End/ 


-- /rangeGame/
-- Precondition for rangeGame k1 k2: k1 <= k2 
rangeGame :: Nat -> Nat -> Game Nat
rangeGame k1 k2 | k1 == k2  = constGame k1
rangeGame k1 k2 = split (Iso ask bld) g1 g2
  where g1 = rangeGame (m+1) k2 
        g2 = rangeGame k1 m 
        ask x = if x > m then Left x else Right x
        bld (Left x) = x 
        bld (Right x) = x 
        m = (k1 + k2) `div` 2 
-- /End/ 


data Tree = Leaf | Node Tree Tree  deriving Show

tiso = Iso ask bld 
  where ask (Leaf)          = Left () 
        ask (Node t1 t2)    = Right (t1,t2) 
        bld (Left ())       = Leaf
        bld (Right (t1,t2)) = Node t1 t2

treeGame1 = split tiso (Single idI) (prodGame treeGame1 treeGame1) 

{-treeGame2 = split tiso (Single idI) (ilGame treeGame2 treeGame2)-}

ones :: [Bit] 
ones = 1:ones

biasedBool = split2 boolIso 3 unitGame 1 unitGame
biasedBoolTriple = prodGame biasedBool (prodGame biasedBool biasedBool)

                           
data Three = A | B | C
threeGame = split3 (flat3 (Iso (\x -> case x of A -> Left (); B -> Right (Left ()); C -> Right (Right ()))
                        (\x -> case x of Left () -> A; Right (Left ()) -> B; Right (Right ()) -> C)))
            1 unitGame 1 unitGame 1 unitGame