{- | This module provides the code of QuickCheck instances, but doesn't declare the instances
   Of the whole orphan deal, oh well. 
 
    Anyway, to use you will need to copy the following code somewhere
> Instance Arbitrary Expr where
>     arbitrary = expr_arb  
>     shrink    = gexpr_shrink
 There is also a gexpr_arb that takes in a generator for the symbol type
-}
module Language.Lambda.Arbitrary where
import Test.QuickCheck
import Control.Applicative ((<*>), (<$>))
import Data.List
import Language.Lambda.AST

-- | An arbitrary function for Expr. See the example above.
expr_arb :: Gen Expr
expr_arb = gexpr_arb sym_arbitrary

-- | Generates a string like "x_{n}" where n is positive integer
sym_arbitrary :: Gen String 
sym_arbitrary = do
    index <- suchThat (arbitrary :: Gen Int) (>0)
    return $ ("x_" ++ (show index))
    
-- | Shrink function for an GExpr. See the example at the top of the module    
gexpr_shrink :: GExpr a -> [GExpr a]
gexpr_shrink x@(Var _) = [x]
gexpr_shrink (App x y) = [x, y]
gexpr_shrink (Lam _ y) = [y]
   
-- | Helper function for creating generators for GExpr. Takes in a generator for the symbol type
gexpr_arb :: Gen a -> Gen (GExpr a) 
gexpr_arb sym_gen = sized $ \x -> gexpr_arb' x sym_gen

-- | Helper function for creating generators for GExpr. Takes in a generator for the symbol type and the
-- "depth" of the expression tree
gexpr_arb' :: Int -> Gen s -> Gen (GExpr s)
gexpr_arb' 0 s_arb = Var <$> s_arb
gexpr_arb' n s_arb = do
    option <- choose(0, 2) :: Gen Int
    case option of
        0 -> Var <$> s_arb
        1 -> App <$> gexpr_arb' (n - 1) s_arb <*> gexpr_arb' (n - 1) s_arb
        2 -> Lam <$> s_arb                    <*> gexpr_arb' (n - 1) s_arb
        _ -> error "choose messed up!"