module Autolib.Util.Generate where

import Autolib.Util.Zufall
import System.Random

-- | erzeugt "zufälligen" dinge fixierter größe
type Generator a = Int -> IO ( a )


-- | annahme: größen verhalten sich additiv
type Operator a = ( Int, [ a ] -> a )

data Config a = 
     Config { forall a. Config a -> [Generator a]
base :: [ Generator a ]
	    , forall a. Config a -> [Operator a]
ops  :: [ Operator  a ]
	    , forall a. Config a -> Int
depth :: Int
	    , forall a. Config a -> Int
size :: Int
	    }

generate :: Config a -> IO ( a )

generate :: forall a. Config a -> IO a
generate Config a
conf | Config a -> Int
forall a. Config a -> Int
depth Config a
conf Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = do
    gen <- [Generator a] -> IO (Generator a)
forall (m :: * -> *) a. RandomC m => [a] -> m a
eins ([Generator a] -> IO (Generator a))
-> [Generator a] -> IO (Generator a)
forall a b. (a -> b) -> a -> b
$ Config a -> [Generator a]
forall a. Config a -> [Generator a]
base Config a
conf
    gen $ size conf

generate Config a
conf = do
    ( arity, op ) <- [(Int, [a] -> a)] -> IO (Int, [a] -> a)
forall (m :: * -> *) a. RandomC m => [a] -> m a
eins ([(Int, [a] -> a)] -> IO (Int, [a] -> a))
-> [(Int, [a] -> a)] -> IO (Int, [a] -> a)
forall a b. (a -> b) -> a -> b
$ Config a -> [(Int, [a] -> a)]
forall a. Config a -> [Operator a]
ops Config a
conf
    if arity > size conf
       then generate conf { depth = 0 }
       else do
	    xs <- summe arity $ size conf
	    gs <- sequence $ do 
	       x <- xs
	       return $ do
	           generate $ conf { depth = pred $ depth conf , size  = x }
	    return $ op gs