{-
  A first example in equalional reasoning. 
  From the definition of append we should be able to 
  semi-automatically prove the three axioms.
 -}

{-@ LIQUID "--no-termination" @-}

module Append where

data L a = N |  C a (L a) deriving (Eq)

data Proof = Proof 


append :: L a -> L a -> L a 
append N xs        = xs
append (C y ys) xs = C y (append ys xs) 


{- All the followin will be autocatically generated by the definition of append
  and a liquid annotation

  axiomatize append

 -}

{-@ measure append :: L a -> L a -> L a @-}
{-@ assume append :: xs:L a -> ys:L a -> {v:L a | v == append xs ys } @-}

{-@ assume axiom_append_nil :: xs:L a -> {v:Proof | append N xs == xs} @-} 
axiom_append_nil :: L a -> Proof 
axiom_append_nil xs = Proof

{-@ assume axiom_append_cons :: x:a -> xs: L a -> ys: L a 
          -> {v:Proof | append (C x xs) ys == C x (append xs ys) } @-} 
axiom_append_cons :: a -> L a -> L a -> Proof 
axiom_append_cons x xs ys = Proof


-- | Proof library: 

{-@ toProof :: l:a -> r:{a|l = r} -> {v:Proof | l = r } @-}
toProof :: a -> a -> Proof
toProof x y = Proof

{-@ eqProof :: l:a -> r:a -> {v:Proof | l = r} -> {v:a | v = l } @-}
eqProof :: a -> a -> Proof -> a 
eqProof x y _ = y 



-- | Proof 1: N is neutral element 

{-@ prop_nil :: xs:L a -> {v:Proof | (append xs N == xs) <=> true } @-}
prop_nil     :: Eq a => L a -> Proof
prop_nil N   =  axiom_append_nil N 

prop_nil (C x xs) = toProof e1 $ eqProof e1 (eqProof e2 e3 pr2) pr1
   where
   	e1  = append (C x xs) N
   	pr1 = prop_nil xs
   	e2  = C x (append xs N)
   	pr2 = prop_nil xs
   	e3  = C x xs


-- | Proof 2: append is associative


{-@ prop_assoc :: xs:L a -> ys:L a -> zs:L a
               -> {v:Proof | (append (append xs ys) zs == append N (append ys zs))} @-}
prop_assoc :: Eq a => L a -> L a -> L a -> Proof
prop_assoc N ys zs = toProof (append (append N ys) zs) $
	                 eqProof (append (append N ys) zs) 
                    (eqProof (append ys zs) 
                             (append N (append ys zs))
                             (axiom_append_nil (append ys zs))
                    )(axiom_append_nil ys)     

prop_assoc (C x xs) ys zs = 
	toProof e1 $ 
	eqProof e1 (eqProof e2 (eqProof e3 (eqProof e4 e5 pr4) pr3) pr2) pr1 
  where
  	e1  = append (append (C x xs) ys) zs
  	pr1 = axiom_append_cons x xs ys
  	e2  = append (C x (append xs ys)) zs
  	pr2 = axiom_append_cons x (append xs ys) zs
  	e3  = C x (append (append xs ys) zs)
  	pr3 = prop_assoc xs ys zs
  	e4  = C x (append xs (append ys zs))
  	pr4 = axiom_append_cons x xs (append ys zs)
  	e5  = append (C x xs) (append ys zs)
