diff --git a/experiment/configuration/BasePredicate.hs b/experiment/configuration/BasePredicate.hs
new file mode 100644
index 0000000000000000000000000000000000000000..1736c96422368c29d0fca7c72b29b73d23f855e3
--- /dev/null
+++ b/experiment/configuration/BasePredicate.hs
@@ -0,0 +1,112 @@
+module BasePredicate (
+  
+  BasePredicate(..),
+  eval,
+  applyOS,
+  applyArch,
+  applyCompiler,
+  applyFlagAssignment,
+  
+  applySystemParams,
+
+  --TODO: move elsewhere:
+  SystemParams(..)
+  
+  ) where
+
+import PartialValuation
+
+import Distribution.Version
+         ( VersionRange(..), withinRange )
+import Distribution.System
+         ( OS(..), Arch(..) )
+import Distribution.Compiler
+         ( CompilerFlavor, CompilerId(..) )
+
+import Distribution.PackageDescription
+         ( FlagName, FlagAssignment )
+
+import Data.Monoid
+         ( Monoid(..) )
+
+data SystemParams = SystemParams OS Arch CompilerId
+
+data BasePredicate
+   = OSPredicate       OS
+   | ArchPredicate     Arch
+   | CompilerPredicate CompilerFlavor VersionRange
+   | FlagPredicate     FlagName
+  deriving (Eq, Show)
+
+-- | Evaluate a 'BasePredicate'. It will return 'Nothing' if and only if the
+-- 'FlagAssignment' is not total.
+--
+-- You can use this along with 'PredicateExpr.eval'' to give a valuation on a
+-- whole 'PredicateExpr':
+--
+-- > PredicateExpr.eval' (eval sysParms flags)
+-- >   :: PredicateExpr BasePredicate -> Maybe Bool
+--
+eval :: SystemParams
+     -> FlagAssignment
+     -> PartialValuation BasePredicate
+eval (SystemParams os arch (CompilerId comp ver)) flagAssignment =
+    PartialValuation peval
+  where
+    peval (OSPredicate   os')             = Just $! os   == os'
+    peval (ArchPredicate arch')           = Just $! arch == arch'
+    peval (CompilerPredicate comp' range) = Just $! comp == comp'
+                                                 && ver `withinRange` range
+    peval (FlagPredicate flag)            = lookup flag flagAssignment
+
+applyOS :: OS -> PartialValuation BasePredicate
+applyOS os = PartialValuation peval
+  where
+    peval (OSPredicate os') = Just $! os == os'
+    peval _                 = Nothing
+
+applyArch :: Arch -> PartialValuation BasePredicate
+applyArch arch = PartialValuation peval
+  where
+    peval (ArchPredicate arch') = Just $! arch == arch'
+    peval _                     = Nothing
+
+applyCompiler :: CompilerId -> PartialValuation BasePredicate
+applyCompiler (CompilerId comp ver) = PartialValuation peval
+  where
+    peval (CompilerPredicate comp' range) = Just $! comp == comp'
+                                                 && ver `withinRange` range
+    peval _                               = Nothing
+
+-- | A 'PartialValuation' on a 'BasePredicate' obtained by applying a
+-- 'FlagAssignment'. It gives a value in the case that the 'BasePredicate' is a
+-- 'FlagPredicate' that is given a value by the 'FlagAssignment'.
+--
+-- This can be used to simplify a compound 'PredicateExpr' using:
+--
+-- > PredicateExpr.simplify (applyFlagAssignment flagAssignment)
+-- >   :: PredicateExpr BasePredicate -> PredicateExpr BasePredicate
+--
+applyFlagAssignment :: FlagAssignment -> PartialValuation BasePredicate
+applyFlagAssignment flagAssignment = PartialValuation peval
+  where
+    peval (FlagPredicate flag)  = lookup flag flagAssignment
+    peval other                 = Nothing
+
+--applyCompilerFlavor :: CompilerFlavor -> PartialValuation BasePredicate
+
+--applyCompilerIds :: [CompilerId] -> PartialValuation BasePredicate
+
+
+-- | Partially evaluate a 'BasePredicate' by supplying values for the
+-- all the 'SystemParams', leaving only the flags.
+--
+applySystemParams :: SystemParams
+                  -> (BasePredicate -> Either Bool FlagName)
+applySystemParams (SystemParams os arch (CompilerId comp ver)) = peval
+  where
+    peval (OSPredicate   os')             = Left $ os   == os'
+    peval (ArchPredicate arch')           = Left $ arch == arch'
+    peval (CompilerPredicate comp' range) = Left $ comp == comp'
+                                                && ver `withinRange` range
+    peval (FlagPredicate flag)            = Right flag
diff --git a/experiment/configuration/ConditionTree.hs b/experiment/configuration/ConditionTree.hs
new file mode 100644
index 0000000000000000000000000000000000000000..eed6845d0ee12efb8d9ed6b9b5c86a3abc3ad206
--- /dev/null
+++ b/experiment/configuration/ConditionTree.hs
@@ -0,0 +1,215 @@
+module ConditionTree (
+    ConditionTree(..),
+    ConditionNode(..),
+    
+    flatten,
+    leaves,
+    unconditional,
+    resolve,
+    resolve',
+    simplify,
+    mapCondition,
+    paths,
+  ) where
+
+import PartialValuation 
+         ( PartialValuation(..), TotalValuation )
+
+import Data.Monoid
+         ( Monoid(..) )
+import qualified Data.Foldable as Foldable
+         ( Foldable(..), toList )
+import Data.Foldable
+         ( Foldable )
+
+-- | A condition tree is a tree of values guarded by condition expressions.
+-- Each level of the tree has a list of nodes.
+--
+newtype ConditionTree condition body
+      = ConditionTree [ConditionNode condition body]
+  deriving (Eq, Show)
+
+-- An individual condition tree node is either a body value or a conditional
+-- with a further tree below. Conditionals can have an else part.
+--
+data ConditionNode condition body
+   = Body             body
+   | IfThen condition (ConditionTree condition body)
+   | IfElse condition (ConditionTree condition body)
+                      (ConditionTree condition body)
+  deriving (Eq, Show)
+
+
+instance Functor (ConditionTree condition) where
+  fmap f (ConditionTree ns) = ConditionTree (map (fmap f) ns)
+
+instance Functor (ConditionNode condition) where
+  fmap f (Body x)          = Body     (f x)
+  fmap f (IfThen c  e)     = IfThen c (fmap f e)
+  fmap f (IfElse c  e1 e2) = IfElse c (fmap f e1) (fmap f e2)
+
+instance Foldable (ConditionTree condition) where
+  foldr f z (ConditionTree ns) = foldr (flip (Foldable.foldr f)) z ns
+
+instance Foldable (ConditionNode condition) where
+  foldr f z (Body      b)     = f b z
+  foldr f z (IfThen  _ e)     = Foldable.foldr f z e
+  foldr f z (IfElse  _ e1 e2) = Foldable.foldr f (Foldable.foldr f z e2) e1
+
+-- | Take all the leaves and flatten them into one value using the monoid
+-- operation.
+--
+flatten :: Monoid body
+        => ConditionTree condition body -> body
+flatten = Foldable.fold
+
+-- | Get a list of all the leaves of the tree.
+--
+leaves :: ConditionTree condition body -> [body]
+leaves = Foldable.toList
+
+-- | Get a list of all the body nodes that are unconditional
+--
+unconditional :: ConditionTree condition body -> [body]
+unconditional (ConditionTree nodes) = [ b | Body b <- nodes ]
+
+{-
+-- | Within each level of the tree, merge all body nodes using the monoid
+-- operation.
+--
+-- Note that this is only valid for commutative monoids
+--
+reorder :: ConditionTree condition body -> ConditionTree condition body
+reorder (ConditionTree nodes) =
+    ConditionTree (body:conditions)
+  where
+    body = mappend [ b | Body b <- nodes ]
+    conditions = 
+    -}
+  
+
+-- | Resolve a condition tree by evaluating all the conditionals and merging
+-- together all those bodies for which the guard conditions are true.
+--
+-- This gives us the semantics of a 'PredicateExpr' so is crucial in the
+-- definition of other properties.
+--
+resolve :: Monoid body
+        => TotalValuation condition
+        -> ConditionTree condition body -> body
+resolve eval = flip evalTree mempty
+  where
+    evalTree (ConditionTree ns) accum = foldr evalNode accum ns
+
+    evalNode (Body body) accum = body `mappend` accum
+
+    evalNode (IfThen condition thenPart) accum
+      | eval condition = evalTree thenPart accum
+      | otherwise      = accum
+
+    evalNode (IfElse condition thenPart elsePart) accum
+      | eval condition = evalTree thenPart accum
+      | otherwise      = evalTree elsePart accum
+
+-- property: in all contexts,
+--   IfThen c t === IfThenElse c t mempty
+
+-- property: in all contexts,
+--   [IfThenElse c t e] === [IfThen c t, IfThen (Not c) e]
+
+-- property:
+--   n === IfThen True [n]
+--   IfThen False ns  === mempty
+
+resolve' :: Monoid body
+         => PartialValuation condition
+         -> ConditionTree condition body -> Maybe body
+resolve' peval = flip evalTree mempty
+  where
+    evalTree (ConditionTree ns) accum = foldr evalNode accum ns
+
+    evalNode (Body body) accum = fmap (body `mappend`) accum
+
+    evalNode (IfThen condition thenPart) accum =
+      case applyPartialValuation peval condition of
+        Just True  -> evalTree thenPart accum
+        Just False -> accum
+        Nothing    -> Nothing
+
+    evalNode (IfElse condition thenPart elsePart) accum =
+      case applyPartialValuation peval condition of
+        Just True  -> evalTree thenPart accum
+        Just False -> evalTree elsePart accum
+        Nothing    -> Nothing
+
+-- | Simplify a condition tree by trying to evaluate or simplify all the
+-- conditional expressions. Whenever a conditional expression can be fully
+-- evaluated then we resolve the conditonal. Otherwise the conditional
+-- expression is just simplified and the conditional left unresolved.
+--
+-- * Property, simplify is an identity if no expressions are changed
+--
+-- > simplify Right = id
+--
+-- * Property, for all condition valuations v
+--
+-- > flatten . simplify (Left . v) = resolve v
+--
+simplify :: (condition -> Either Bool condition')
+          -> ConditionTree condition  body
+          -> ConditionTree condition' body
+simplify simplifyCondition = simplifyTree
+  where
+    simplifyTree  ns = ConditionTree (simplifyTree' ns [])
+    simplifyTree' (ConditionTree ns) ns' = foldr simplifyNode ns' ns
+
+    simplifyNode (Body body) ns = Body body : ns
+
+    simplifyNode (IfThen condition thenPart) ns =
+      case simplifyCondition condition of
+        Left  True       -> simplifyTree' thenPart ns
+        Left  False      -> ns
+        Right condition' -> IfThen condition' (simplifyTree thenPart) : ns
+
+    simplifyNode (IfElse condition thenPart elsePart) ns =
+      case simplifyCondition condition of
+        Left  True       -> simplifyTree' thenPart ns
+        Left  False      -> simplifyTree' elsePart ns
+        Right condition' -> IfElse condition' (simplifyTree thenPart)
+                                              (simplifyTree elsePart) : ns
+
+-- property: simplify (Left . f) == ConditionTree [ns] where all ns isbody
+-- that is, if we simplify every conditional we should end up with a (possibly
+-- empty) list of bodies (no conditionals).
+
+-- property: simplify Right = id
+
+mapCondition :: (condition -> condition')
+             -> ConditionTree condition  body
+             -> ConditionTree condition' body
+mapCondition f = simplify (Right . f)
+
+-- property: canonical t => mapCondition id t = t
+
+-- | All the leaves of the condition tree along with the conditions along the
+-- path to that leaf.
+--
+-- Effectively this gives an alternative representation of the tree but without
+-- any of the sharing of conditions that we get from the tree structure.
+--
+paths :: (condition -> condition)
+      -> ConditionTree condition body
+      -> [([condition], body)]
+paths cnot = leaves . pathsTree []
+  where
+    pathsTree path (ConditionTree ns) = ConditionTree (map (pathsNode path) ns)
+
+    pathsNode path (Body body) =
+      Body (path, body)
+
+    pathsNode path (IfThen condition thenPart) =
+      IfThen condition (pathsTree (condition:path) thenPart)
+
+    pathsNode path (IfElse condition thenPart elsePart) =
+      IfElse condition (pathsTree (     condition:path) thenPart)
+                       (pathsTree (cnot condition:path) elsePart)
diff --git a/experiment/configuration/Configuration.hs b/experiment/configuration/Configuration.hs
new file mode 100644
index 0000000000000000000000000000000000000000..2e0e5dd2294c9c751a1f4c076fb443e31bd39408
--- /dev/null
+++ b/experiment/configuration/Configuration.hs
@@ -0,0 +1,214 @@
+module Configuration where
+
+import qualified Predicate
+import Predicate (Predicate)
+
+import qualified ConditionTree
+import qualified PredicateExpr
+import PredicateExpr (PredicateExpr)
+import ConditionTree (ConditionTree)
+import qualified BasePredicate
+import BasePredicate (BasePredicate, SystemParams)
+import PartialValuation (PartialValuation, TotalValuation)
+
+import Distribution.Package
+         ( Dependency(..), PackageName )
+import Distribution.Version
+         ( VersionRange )
+
+import Distribution.PackageDescription
+         ( FlagName, FlagAssignment )
+
+import Data.Monoid
+         ( Monoid(..) )
+import Data.Foldable
+         ( Foldable(fold) )
+import Data.List
+         ( partition )
+import Data.Maybe
+         ( isNothing )
+import qualified Data.Map as Map
+import Data.Map (Map)
+         
+
+flatten :: GenericPackageDescription -> PackageDescription
+flatten = PackageDescription . ConditionTree.flatten . pkgCondTree
+
+-- | Finalise a generic package description by resolving all the conditonals.
+-- It requires a full flag assignment. Use 'search' if you do not know the
+-- flags or only have a partial flag assignment.
+--
+-- It returns Nothing if and only if the flag assignment is does not to cover
+-- all flags used in conditions. (CHECK this! does short-cut evaluation mean
+-- some flags may not be needed?)
+--
+finalise :: SystemParams -> FlagAssignment
+         -> GenericPackageDescription -> Maybe PackageDescription
+finalise sysParams flagAssignment =
+    fmap PackageDescription
+  . ConditionTree.resolve' (Predicate.eval sysParams flagAssignment)
+  . pkgCondTree
+
+--apply partial flag assignment
+-- :: FlagAssignment
+-- -> GenericPackageDescription -> GenericPackageDescription
+simplify :: PartialValuation BasePredicate
+         -> GenericPackageDescription -> GenericPackageDescription
+simplify v pkg =
+    pkg {
+      pkgCondTree = ConditionTree.simplify evalOrSimplify (pkgCondTree pkg)
+    }
+  where
+    evalOrSimplify :: Predicate -> Either Bool Predicate
+    evalOrSimplify cond = case PredicateExpr.simplify v cond of
+      PredicateExpr.PredExprLit b -> Left b
+      other                       -> Right other
+    
+type ResolveError = ()
+
+resolve :: FlagAssignment
+        -> (Constraint -> Bool)
+        -> SystemParams
+        -> [Constraint]
+        -> GenericPackageDescription
+        -> Either ResolveError FlagAssignment
+resolve initialFlags satisfyDep sysParams extraConstraints pkg =
+    search
+      satisfiable
+      flagDoms
+      (simplifyTree (pkgCondTree pkg))
+
+  where
+    mkConstraints :: BuildInfo -> Constraints
+    mkConstraints = undefined
+
+    satisfiable   :: Constraints -> Maybe ResolveError
+    satisfiable   SatisfiableConstraints   = Nothing
+    satisfiable   UnsatisfiableConstraints = Just ()
+
+    flagDoms      :: [FlagDomain]   
+    flagDoms      = undefined
+
+    --TODO: apply the initial flag assignment, and manual flags
+    --      extract the remaining used flags after simplifying
+    --      construct all test flag assignments
+
+    --TODO: merge constraints on the same level
+
+    simplifyTree :: ConditionTree (PredicateExpr BasePredicate) BuildInfo
+                 -> ConditionTree (PredicateExpr FlagName)      Constraints
+    simplifyTree = fmap mkConstraints
+                 . ConditionTree.simplify (literal . simplifyExpr)
+      where
+        literal (PredicateExpr.PredExprLit b) = Left b
+        literal e                             = Right e
+
+    simplifyExpr :: PredicateExpr BasePredicate
+                  -> PredicateExpr FlagName
+    simplifyExpr =
+      PredicateExpr.simplify' (BasePredicate.applySystemParams sysParams)
+
+
+search :: forall constraints error
+        . Monoid constraints
+       => (constraints -> Maybe error)
+       -> [FlagDomain]
+       -> ConditionTree (PredicateExpr FlagName) constraints
+       -> Either error FlagAssignment
+search satisfiable flagDoms tree =
+    case satisfiable (mconcat (ConditionTree.unconditional tree)) of
+      Just err -> Left err
+      Nothing  -> tryAllAssignments Nothing assignments
+
+  where
+    -- Find the first assignment that works,
+    -- though if all fail then return the first failure
+    tryAllAssignments (Just err) []     = Left err
+    tryAllAssignments Nothing    []     = error "impossible"
+    tryAllAssignments merr       (a:as) =
+      case checkAssignment a tree of
+        Just err' | isNothing merr -> tryAllAssignments (Just err') as
+                  | otherwise      -> tryAllAssignments merr        as
+        Nothing   -> Right a
+
+    assignments = allAssignments (reorderFlagDomains flagDoms)
+
+    checkAssignment :: FlagAssignment
+                    -> ConditionTree (PredicateExpr FlagName) constraints
+                    -> Maybe error
+    checkAssignment flagAssignment =
+        satisfiable
+      . ConditionTree.resolve (PredicateExpr.eval (evalFlags flagAssignment))
+
+    evalFlags :: FlagAssignment -> TotalValuation FlagName
+    evalFlags flagAssignment flag =
+      case lookup flag flagAssignment of
+        Just val -> val
+        Nothing  -> error "missing flag"
+
+    -- | Generate all possible flag assignements given the domain of each flag
+    allAssignments :: [FlagDomain] -> [FlagAssignment]
+    allAssignments []                 = [ [] ]
+    allAssignments ((flag, dom):doms) = [ (flag, val):flags
+                                        | val   <- dom
+                                        , flags <- allAssignments doms ]
+
+    -- | If flags can only take one possible value then put them first
+    reorderFlagDomains :: [FlagDomain] -> [FlagDomain]
+    reorderFlagDomains doms =
+      let (singles, rest) = partition ((==1) . length . snd) doms
+       in singles ++ rest
+
+
+type FlagDomain = (FlagName, [Bool])
+
+
+data GenericPackageDescription = GenericPackageDescription {
+  flags       :: [Flag],
+  pkgCondTree :: ConditionTree Predicate BuildInfo
+}
+ 
+data Flag = Flag {
+    flagName        :: FlagName,
+    flagDefault     :: Bool,
+    flagManual      :: Bool
+  }
+  deriving (Show, Eq)
+
+data PackageDescription = PackageDescription {
+    buildInfo :: BuildInfo
+  }
+
+data BuildInfo = BuildInfo {
+    buildDepends      :: [Dependency],
+    pkgconfigDepends  :: [Dependency],
+    extralibs         :: [String]
+  }
+
+instance Monoid BuildInfo where
+  mempty       = BuildInfo {
+    buildDepends      = [],
+    pkgconfigDepends  = [],
+    extralibs         = []
+  }
+  mappend a b = BuildInfo {
+    buildDepends      = combine buildDepends,
+    pkgconfigDepends  = combine pkgconfigDepends,
+    extralibs         = combine extralibs
+  }
+    where combine f = f a `mappend` f b
+
+data Constraint = HaskellLibPackage PackageName VersionRange
+                | PkgConfigPackage  PackageName VersionRange
+                | CLib String
+                | BuildTool String
+
+data Constraints
+   = SatisfiableConstraints {
+       hsLibVersions     :: Map PackageName VersionRange,
+       pkgConfigVersions :: Map PackageName VersionRange
+       
+     }
+   | UnsatisfiableConstraints
+
+instance Monoid Constraints where
diff --git a/experiment/configuration/PartialValuation.hs b/experiment/configuration/PartialValuation.hs
new file mode 100644
index 0000000000000000000000000000000000000000..53b09bf1cfcd83a0c3f698dd532b7389d9468531
--- /dev/null
+++ b/experiment/configuration/PartialValuation.hs
@@ -0,0 +1,62 @@
+module PartialValuation where
+
+import Data.Monoid
+         ( Monoid(..) )
+import Control.Monad
+         ( liftM, liftM2 )
+import qualified Data.Foldable as Foldable
+         ( Foldable(foldMap, foldr), toList )
+
+import Prelude hiding (null)
+
+-- | A valuation on some base predicate type.
+--
+type TotalValuation base = (base -> Bool)
+
+-- | A partial valuation on some base predicate. This is useful when we have
+-- only partial information but still wish to simplify an expression using the
+-- predicates.
+--
+-- In particular, a partial valuation is a 'Monoid' via 'null' and 'extend'.
+--
+newtype PartialValuation base
+      = PartialValuation { applyPartialValuation :: base -> Maybe Bool }
+
+-- | A null valuation. It gives no value for all predicates.
+--
+null :: PartialValuation base
+null = PartialValuation (const Nothing)
+
+-- | Extend one partial valuation with another.
+--
+-- In the valuation @v' `extend` v@, the valuation @v@ is being extended by the
+-- valuation @v'@. That means values returned by the first take precedence over
+-- those returned by the second.
+--
+extend :: PartialValuation base
+       -> PartialValuation base
+       -> PartialValuation base
+extend (PartialValuation v) (PartialValuation v') =
+    PartialValuation $ \x -> v x `override` v' x
+  where
+    override a@(Just _) _ = a
+    override _          b = b
+
+instance Monoid (PartialValuation base) where
+  mempty  = null
+  mappend = extend
+
+
+-- | Extend one a total valuation with a partial valuation to give a total
+-- valuation.
+--
+-- Values from the partial valuation take precedence.
+--
+extend' :: PartialValuation base
+        -> TotalValuation base
+        -> TotalValuation base
+extend' (PartialValuation v) v' =
+  \x -> v x `override` v' x
+  where
+    override (Just a) _ = a
+    override _        b = b
diff --git a/experiment/configuration/Predicate.hs b/experiment/configuration/Predicate.hs
new file mode 100644
index 0000000000000000000000000000000000000000..a4829a320c9779dd03417f92c16f900557d9abb6
--- /dev/null
+++ b/experiment/configuration/Predicate.hs
@@ -0,0 +1,38 @@
+module Predicate (
+
+  Predicate,
+  eval,
+  applySystemParams,
+  applyFlagAssignment,
+
+  ) where
+
+import qualified BasePredicate
+import BasePredicate (BasePredicate, SystemParams)
+
+import qualified PredicateExpr
+import PredicateExpr (PredicateExpr)
+
+import PartialValuation
+         ( PartialValuation )
+
+import Distribution.PackageDescription
+         ( FlagName, FlagAssignment )
+
+type Predicate = PredicateExpr BasePredicate
+
+eval :: SystemParams
+     -> FlagAssignment
+     -> PartialValuation Predicate
+eval sysParams flagAssignment =
+  PredicateExpr.eval' (BasePredicate.eval sysParams flagAssignment)
+
+applySystemParams :: SystemParams
+                  -> Predicate -> PredicateExpr FlagName
+applySystemParams sysParams =
+  PredicateExpr.simplify' (BasePredicate.applySystemParams sysParams)
+
+applyFlagAssignment :: FlagAssignment
+                    -> Predicate -> Predicate
+applyFlagAssignment flagAssignment =
+  PredicateExpr.simplify (BasePredicate.applyFlagAssignment flagAssignment)
diff --git a/experiment/configuration/PredicateExpr.hs b/experiment/configuration/PredicateExpr.hs
new file mode 100644
index 0000000000000000000000000000000000000000..b9ca2924910117d7fdc1aa2d0e14d01e922f21ad
--- /dev/null
+++ b/experiment/configuration/PredicateExpr.hs
@@ -0,0 +1,192 @@
+module PredicateExpr (
+
+  PredicateExpr(..),
+  fold,
+
+  eval, eval',
+  simplify, simplify',
+
+  basePredicates,
+
+
+  PartialValuation(..),
+  ) where
+
+import PartialValuation
+
+import Data.Monoid
+         ( Monoid(..) )
+import Control.Monad
+         ( liftM, liftM2 )
+import qualified Data.Foldable as Foldable
+         ( Foldable(foldMap, foldr), toList )
+
+-- | A predicate expression. It is parameterised over the type of the base
+-- predicate.
+--
+data PredicateExpr base
+   = PredExprBase base
+   | PredExprLit  Bool
+   | PredExprNot  (PredicateExpr base)
+   | PredExprAnd  (PredicateExpr base) (PredicateExpr base)
+   | PredExprOr   (PredicateExpr base) (PredicateExpr base)
+  deriving (Eq, Show)
+
+-- | The natural fold on the 'PredicateExpr' type.
+--
+-- For example, an evaluation function is just:
+--
+-- > eval :: PredicateExpr BasePredicate -> Bool
+-- > eval = fold BasePredicate.eval id not (&&) (||)
+--
+fold :: (base -> a) -> (Bool -> a)
+     -> (a -> a) -> (a -> a -> a) -> (a -> a -> a)
+     -> PredicateExpr base -> a
+fold fbase flit fnot fand for = fold'
+  where
+    fold' (PredExprBase base) = fbase base
+    fold' (PredExprLit  lit)  = flit  lit
+    fold' (PredExprNot  a)    = fnot  (fold' a)
+    fold' (PredExprAnd  a b)  = fand  (fold' a) (fold' b)
+    fold' (PredExprOr   a b)  = for   (fold' a) (fold' b)
+{-# INLINE fold #-}
+
+instance Functor PredicateExpr where
+  fmap f = fold (PredExprBase . f)
+                PredExprLit PredExprNot PredExprAnd PredExprOr
+
+instance Foldable.Foldable PredicateExpr where
+  foldMap f = fold f (const mempty) id mappend mappend
+
+  foldr f z (PredExprBase b)  = f b z
+  foldr f z (PredExprLit _)   = z
+  foldr f z (PredExprNot a)   = Foldable.foldr f z a
+  foldr f z (PredExprAnd a b) = Foldable.foldr f (Foldable.foldr f z b) a
+  foldr f z (PredExprOr  a b) = Foldable.foldr f (Foldable.foldr f z b) a
+
+-- | List all the base predicates occuring in the expression.
+--
+basePredicates :: PredicateExpr base -> [base]
+basePredicates = Foldable.toList
+
+-- | Given a valuation on the base predicate, extend it to a valuation on the
+-- whole 'PredicateExpr'. This gives the semantics of a 'PredicateExpr'
+--
+eval :: TotalValuation base
+     -> TotalValuation (PredicateExpr base)
+eval evalBase = fold evalBase id not (&&) (||)
+
+-- | Given a partial valuation on the base predicate, extend it to a partial
+-- valuation on the whole 'PredicateExpr'.
+--
+-- * Property: with a total valuation for the base predicate, 'eval'' is the
+-- same as 'eval':
+--
+-- > eval' (Just . v) = Just . eval v
+--
+-- * Property: the evaluation is 'Nothing' if and only if the valuation is
+-- 'Nothing' on any value in the expression
+--
+-- >   eval' v e == Nothing
+-- > = any (\e' -> v e' == Nothing) (basePredicates e)
+--
+eval' :: PartialValuation base
+      -> PartialValuation (PredicateExpr base)
+eval' (PartialValuation evalBase) = PartialValuation $
+  fold evalBase return (liftM not) (liftM2 (&&)) (liftM2 (||))
+
+-- | Simplify a 'PredicateExpr', given a function to evaluate or simplify
+-- base predicates. It can do some simple algebraic simplification even if no
+-- base predicates are evaluated.
+--
+-- * Property: simplify is the syntactic equivalent of evaluation. For any
+-- partial valuation, using that to syntacticly simplify gives an expression
+-- with the same set of valuations as the original expression with the partial
+-- valuation extend to a full valuation.
+--
+-- > eval v (simplify (toEither v') e) = eval (extend v' v) e
+-- >   where
+-- >     extend f' f = \x -> f' x `override` f x
+-- >     override    = flip fromMaybe
+-- >
+-- >     toEither  f = \x -> maybe (Right x) Left (f x)
+-- >     toEither :: (b -> Maybe  a  )
+-- >              -> (b -> Either a b)
+--
+-- * Property: when doing no evaluation of the base predicate, simplify does
+-- not change the valuations of the expression. Or to put it another way,
+-- @simplify Right@ is an identify function on the semantics of expressions.
+-- Note this property is a simple consequence of the first property. If we pick
+-- @v' x = Nothing@ then we can derive this property.
+--
+-- > eval v (simplify Right e) = eval v e
+--
+-- * Property: when doing full evaluation of the base predicate, simplify does
+-- sufficient simplification to be equivalent to full evaluation using 'eval'.
+--
+-- > simplify (Left . v) e == PredExprLit (eval v e)
+--
+-- * Property: when doing no evaluation of the base predicate, simplify
+-- eliminates all True and False literals occuring as subexpressions.
+--
+-- > not (literalAsSubExpr (simplify Right e))
+-- >   where
+-- >     literalAsSubExpr (PredExprLit _) = False
+-- >     literalAsSubExpr e = PredicateExpr.fold (const False) (const True)
+-- >                                             id (||) (||) e
+--
+-- The first property tells us simplify does correct simplification though
+-- not necessarily that it does any evaluation at all. The third and fourth
+-- properties tell us that it's doing enough simplification. Note that due to
+-- the symmetry of '&&' and '||', the second property is not enough to tell us
+-- we're doing full simplification. Full evaluation only needs to reduce on one
+-- side of '&&' and '||', not on both. The third property shows we're
+-- simplifying on both sides.
+--
+-- * Property: idempotentcy, at least for a partial valuation that keeps the
+-- old expression when it gives it no value. In other words 'simplify' does
+-- all it's work in one go.
+--
+-- > simplify v' . simplify v' = simplify v'
+-- >   where
+-- >     v' = toEither v
+-- >     toEither  f x = maybe (Right x) Left (f x)
+-- >     toEither :: (b -> Maybe  a  )
+-- >              -> (b -> Either a b)
+--
+simplify :: PartialValuation base
+         -> PredicateExpr base
+         -> PredicateExpr base
+simplify v = simplify' v'
+  where
+    v' x = maybe (Right x) Left (applyPartialValuation v x)
+
+simplify' :: (base -> Either Bool base')
+          -> PredicateExpr base
+          -> PredicateExpr base'
+simplify' valuation =
+  fold simplifyBase simplifyLit
+       simplifyNot simplifyAnd simplifyOr
+  where
+    simplifyBase predicate =
+      case valuation predicate of
+        Left  bool       -> PredExprLit  bool
+        Right predicate' -> PredExprBase predicate'
+
+    simplifyLit lit   = PredExprLit lit
+
+    simplifyNot (PredExprLit lit) = PredExprLit (not lit)
+    simplifyNot (PredExprNot e)   = e
+    simplifyNot e                 = PredExprNot e
+
+    simplifyAnd (PredExprLit True)  e2 = e2
+    simplifyAnd (PredExprLit False) _  = PredExprLit False
+    simplifyAnd e1 (PredExprLit True)  = e1
+    simplifyAnd _  (PredExprLit False) = PredExprLit False
+    simplifyAnd e1 e2                  = PredExprAnd e1 e2
+
+    simplifyOr (PredExprLit True)  _  = PredExprLit True
+    simplifyOr (PredExprLit False) e2 = e2
+    simplifyOr _  (PredExprLit True)  = PredExprLit True
+    simplifyOr e1 (PredExprLit False) = e1
+    simplifyOr e1 e2                  = PredExprOr e1 e2
diff --git a/experiment/configuration/Test/BasePredicate.hs b/experiment/configuration/Test/BasePredicate.hs
new file mode 100644
index 0000000000000000000000000000000000000000..11b4603e53875784fc377c4d6813c0a2450d1ae9
--- /dev/null
+++ b/experiment/configuration/Test/BasePredicate.hs
@@ -0,0 +1,44 @@
+module Test.BasePredicate where
+
+import BasePredicate
+import PartialValuation
+
+import Test.QuickCheck
+
+import Control.Monad (liftM, liftM2, liftM3)
+import Data.Monoid
+         ( Monoid(..) )
+
+instance Arbitrary SystemParams where
+  arbitrary = liftM3 SystemParams arbitrary arbitrary arbitrary
+  shrink (SystemParams os arch compid) =
+       [ SystemParams os' arch  compid  | os' <- shrink os ]
+    ++ [ SystemParams os  arch' compid  | arch' <- shrink arch ]
+    ++ [ SystemParams os  arch  compid' | compid' <- shrink compid ]
+
+instance Arbitrary BasePredicate where
+  arbitrary = frequency
+    [ (1, liftM  OSPredicate arbitrary)
+    , (1, liftM  ArchPredicate arbitrary)
+    , (1, liftM2 CompilerPredicate arbitrary arbitrary)
+    , (2, liftM  FlagPredicate arbitrary)
+    ]
+  shrink (OSPredicate   os  ) = [ OSPredicate   os'   | os'   <- shrink os   ]
+  shrink (ArchPredicate arch) = [ ArchPredicate arch' | arch' <- shrink arch ]
+  shrink (CompilerPredicate comp range) =
+                   [ CompilerPredicate comp' range  | comp'  <- shrink comp  ]
+                ++ [ CompilerPredicate comp  range' | range' <- shrink range ]
+  shrink (FlagPredicate flag) = [ FlagPredicate flag' | flag' <- shrink flag ]
+
+
+v === v' = forAll arbitrary $ \e -> applyPartialValuation v  e
+                                 == applyPartialValuation v' e
+
+prop_eval sysParams flags = eval  sysParams flags
+                        === eval' sysParams flags
+  where
+    eval' (SystemParams os arch comp) flagAssignment =
+                applyOS os
+      `mappend` applyArch arch
+      `mappend` applyCompiler comp
+      `mappend` applyFlagAssignment flagAssignment
diff --git a/experiment/configuration/Test/ConditionTree.hs b/experiment/configuration/Test/ConditionTree.hs
new file mode 100644
index 0000000000000000000000000000000000000000..a3335b65720bce07a792095f9feeb912eca6a2e1
--- /dev/null
+++ b/experiment/configuration/Test/ConditionTree.hs
@@ -0,0 +1,78 @@
+module Test.ConditionTree where
+
+import ConditionTree
+import Test.QuickCheck
+import qualified Test.Laws as Laws
+import Test.Poly (A, B, C, M)
+
+import Control.Monad (liftM, liftM2, liftM3)
+
+import qualified Data.Foldable as Foldable
+import Data.Monoid (Sum(..))
+
+instance (Arbitrary condition, Arbitrary body)
+      => Arbitrary (ConditionTree condition body) where
+  arbitrary = sized $ \n -> do
+    k <- choose (0,n)
+    liftM ConditionTree $
+      sequence [ resize (n `div` k) arbitrary | _ <- [1..k] ]
+
+  shrink (ConditionTree ns) = [ ConditionTree ns' | ns' <- shrink ns ]
+
+instance (Arbitrary condition, Arbitrary body)
+      => Arbitrary (ConditionNode condition body) where
+  arbitrary = sized arbNode
+    where
+      arbNode n = frequency $
+        [ (2, liftM Body arbitrary)
+        ] ++ concat
+        [ [ (1, liftM2 IfThen arbitrary arbTree1)
+          , (1, liftM3 IfElse arbitrary arbTree2 arbTree2)
+          ]
+        | n > 0
+        ]
+        where
+          arbTree1 = resize (n `div` 2) arbitrary
+          arbTree2 = resize (n `div` 2) arbitrary
+
+  shrink (Body   b)       = [ Body b' | b' <- shrink b ]
+  shrink (IfThen c e1)    = (case e1 of ConditionTree x -> x)
+                         ++ [ IfThen c' e1  | c'  <- shrink c  ]
+                         ++ [ IfThen c  e1' | e1' <- shrink e1 ]
+  shrink (IfElse c e1 e2) = (case e1 of ConditionTree x -> x)
+                         ++ (case e2 of ConditionTree x -> x)
+                         ++ [ IfElse c' e1  e2  | c'  <- shrink c  ]
+                         ++ [ IfElse c  e1' e2  | e1' <- shrink e1 ]
+                         ++ [ IfElse c  e1  e2' | e2' <- shrink e2 ]
+  
+prop_tree_fmap_1 :: ConditionTree C M -> Bool
+prop_tree_fmap_1 = Laws.fmap_1
+
+prop_tree_fmap_2 :: (A -> B) -> (M -> A) -> ConditionTree C M -> Bool
+prop_tree_fmap_2 = Laws.fmap_2
+
+prop_node_fmap_1 :: ConditionNode C M -> Bool
+prop_node_fmap_1 = Laws.fmap_1
+
+prop_node_fmap_2 :: (A -> B) -> (M -> A) -> ConditionNode C M -> Bool
+prop_node_fmap_2 = Laws.fmap_2
+
+prop_tree_foldable_1 :: ConditionTree C M -> Bool 
+prop_tree_foldable_1 = Laws.foldable_1
+
+prop_tree_foldable_2 :: (A -> B -> B) -> B -> ConditionTree C A -> Bool
+prop_tree_foldable_2 = Laws.foldable_2
+
+prop_node_foldable_1 :: ConditionNode C M -> Bool 
+prop_node_foldable_1 = Laws.foldable_1
+
+prop_node_foldable_2 :: (A -> B -> B) -> B -> ConditionNode C A -> Bool
+prop_node_foldable_2 = Laws.foldable_2
+
+prop_resolve_1 :: (C -> Bool) -> ConditionTree.ConditionTree C M -> Bool
+prop_resolve_1 v t = resolve v t == flatten (simplify (Left . v) t)
+
+prop_simplify_1 t = simplify Right t == t
+
+size :: ConditionNode a b -> Int
+size = length . Foldable.toList
diff --git a/experiment/configuration/Test/Configuration.hs b/experiment/configuration/Test/Configuration.hs
new file mode 100644
index 0000000000000000000000000000000000000000..a361242a820695ee8760fc7d594e63488ed0b528
--- /dev/null
+++ b/experiment/configuration/Test/Configuration.hs
@@ -0,0 +1,63 @@
+module Test.Configuration where
+
+import qualified Predicate
+import Predicate (Predicate)
+
+import BasePredicate (SystemParams)
+
+import Distribution.Package
+         ( Dependency(..) )
+
+import Distribution.PackageDescription
+         ( FlagAssignment )
+
+import Data.Monoid
+         ( Monoid(..) )
+import Data.Foldable
+         ( Foldable(fold) )
+
+flatten :: GenericPackageDescription -> PackageDescription
+flatten = PackageDescription . flattenConditionTree
+
+finalise :: FlagAssignment -> SystemParams
+         -> GenericPackageDescription -> PackageDescription
+finalise = error "TODO: finalise"
+
+--apply partial flag assignment
+-- :: FlagAssignment
+-- -> GenericPackageDescription -> GenericPackageDescription
+
+type DepInfo = ()
+
+search :: SystemParams -> DepInfo
+       -> GenericPackageDescription -> Maybe FlagAssignment
+search = error "TODO: search"
+
+
+
+type GenericPackageDescription
+   = ConditionTree Predicate BuildInfo
+ 
+
+data PackageDescription = PackageDescription {
+  buildInfo :: BuildInfo
+}
+
+data BuildInfo = BuildInfo  {
+  buildDepends      :: [Dependency],
+  pkgconfigDepends  :: [Dependency],
+  extralibs         :: [String]
+}
+
+instance Monoid BuildInfo where
+  mempty       = BuildInfo {
+    buildDepends      = [],
+    pkgconfigDepends  = [],
+    extralibs         = []
+  }
+  mappend a b = BuildInfo {
+    buildDepends      = combine buildDepends,
+    pkgconfigDepends  = combine pkgconfigDepends,
+    extralibs         = combine extralibs
+  }
+    where combine f = f a `mappend` f b
diff --git a/experiment/configuration/Test/Laws.hs b/experiment/configuration/Test/Laws.hs
new file mode 100644
index 0000000000000000000000000000000000000000..62450dbe725428bbeef2c8ff6148dc969515cbcb
--- /dev/null
+++ b/experiment/configuration/Test/Laws.hs
@@ -0,0 +1,64 @@
+module Test.Laws where
+
+import Prelude
+import Prelude (Functor(..))
+import Data.Monoid (Monoid(..), Endo(..))
+import qualified Data.Foldable as Foldable
+
+-- | The first 'fmap' law
+-- 
+-- > fmap id  ==  id
+--
+fmap_1 :: (Eq (f a), Functor f) => f a -> Bool
+fmap_1 x = fmap id x == x
+
+-- | The second 'fmap' law
+-- 
+-- > fmap (f . g)  ==  fmap f . fmap g
+--
+fmap_2 :: (Eq (f c), Functor f) => (b -> c) -> (a -> b) -> f a -> Bool
+fmap_2 f g x = fmap (f . g) x == (fmap f . fmap g) x
+
+
+-- | The monoid identity law, 'mempty' is a left and right identity of
+-- 'mappend':
+--
+-- > mempty `mappend` x = x
+-- > x `mappend` mempty = x
+--
+monoid_1 :: (Eq a, Data.Monoid.Monoid a) => a -> Bool
+monoid_1 x = mempty `mappend` x == x
+          && x `mappend` mempty == x
+
+-- | The monoid associativity law, 'mappend' must be associative.
+--
+-- > (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
+--
+monoid_2 :: (Eq a, Data.Monoid.Monoid a) => a -> a -> a -> Bool
+monoid_2 x y z = (x `mappend`  y) `mappend` z
+              ==  x `mappend` (y  `mappend` z)
+
+-- | The 'mconcat' definition. It can be overidden for the sake of effeciency
+-- but it must still satisfy the property given by the default definition:
+--
+-- > mconcat = foldr mappend mempty
+--
+monoid_3 :: (Eq a, Data.Monoid.Monoid a) => [a] -> Bool
+monoid_3 xs = mconcat xs == foldr mappend mempty xs
+
+
+-- | First 'Foldable' law
+--
+-- > Foldable.fold = Foldable.foldr mappend mempty
+--
+foldable_1 :: (Foldable.Foldable t, Monoid m, Eq m) => t m -> Bool
+foldable_1 x = Foldable.fold x == Foldable.foldr mappend mempty x
+
+-- | Second 'Foldable' law
+--
+-- > foldr f z t = appEndo (foldMap (Endo . f) t) z
+--
+foldable_2 :: (Foldable.Foldable t, Eq b)
+               => (a -> b -> b) -> b -> t a -> Bool
+foldable_2 f z t = Foldable.foldr f z t
+                    == appEndo (Foldable.foldMap (Endo . f) t) z
diff --git a/experiment/configuration/Test/PartialValuation.hs b/experiment/configuration/Test/PartialValuation.hs
new file mode 100644
index 0000000000000000000000000000000000000000..47b1f0e2a0c778b4bb6717cc59b075ba3b0c2200
--- /dev/null
+++ b/experiment/configuration/Test/PartialValuation.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE StandaloneDeriving #-}
+module Test.PartialValuation where
+
+import BasePredicate
+import PartialValuation
+
+import Test.QuickCheck
+import Test.Poly (A)
+
+import Data.Monoid
+         ( Monoid(..) )
+
+
+instance CoArbitrary a => Arbitrary (PartialValuation a) where
+  arbitrary = fmap PartialValuation arbitrary
+
+deriving instance Show (PartialValuation a)
+
+infix 4 ===
+
+(===) :: (Arbitrary a, Show a) => PartialValuation a -> PartialValuation a -> Property
+v === v' = forAll arbitrary $ \e ->
+                applyPartialValuation v  e
+             == applyPartialValuation v' e
+
+prop_monoid_1 :: PartialValuation A -> Property
+prop_monoid_1 v = mempty `mappend` v === v
+
+prop_monoid_2 :: PartialValuation A -> Property
+prop_monoid_2 v = v `mappend` mempty === v
+
+prop_monoid_3 :: PartialValuation A -> PartialValuation A -> PartialValuation A -> Property
+prop_monoid_3 x y z = (x `mappend`  y) `mappend` z
+                  ===  x `mappend` (y  `mappend` z)
diff --git a/experiment/configuration/Test/Poly.hs b/experiment/configuration/Test/Poly.hs
new file mode 100644
index 0000000000000000000000000000000000000000..975e453932b1a248ac15df08e518d06c963770cd
--- /dev/null
+++ b/experiment/configuration/Test/Poly.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+module Test.Poly where
+
+import Data.Monoid (Monoid)
+
+import Test.QuickCheck (Arbitrary, CoArbitrary)
+import Text.Show.Functions
+
+newtype A = A Int deriving (Eq, Show, Arbitrary, CoArbitrary)
+newtype B = B Int deriving (Eq, Show, Arbitrary, CoArbitrary)
+newtype C = C Int deriving (Eq, Show, Arbitrary, CoArbitrary)
+
+newtype M = M [Int] deriving (Eq, Show, Arbitrary, CoArbitrary, Monoid)
diff --git a/experiment/configuration/Test/Predicate.hs b/experiment/configuration/Test/Predicate.hs
new file mode 100644
index 0000000000000000000000000000000000000000..88ac786aa279c5c64c577aeac07a4cec061b4902
--- /dev/null
+++ b/experiment/configuration/Test/Predicate.hs
@@ -0,0 +1,35 @@
+module Test.Predicate (
+
+  Predicate,
+  eval,
+  applySystemParams,
+  applyFlagAssignment,
+
+  ) where
+
+import qualified BasePredicate
+import BasePredicate (BasePredicate, SystemParams)
+
+import qualified PredicateExpr
+import PredicateExpr (PredicateExpr)
+
+import Distribution.PackageDescription
+         ( FlagName, FlagAssignment )
+
+type Predicate = PredicateExpr BasePredicate
+
+eval :: SystemParams
+     -> FlagAssignment
+     -> Predicate -> Maybe Bool
+eval sysParams flagAssignment =
+  PredicateExpr.eval' (BasePredicate.eval sysParams flagAssignment)
+
+applySystemParams :: SystemParams
+                  -> Predicate -> PredicateExpr FlagName
+applySystemParams sysParams =
+  PredicateExpr.simplify (BasePredicate.applySystemParams sysParams)
+
+applyFlagAssignment :: FlagAssignment
+                    -> Predicate -> Predicate
+applyFlagAssignment flagAssignment =
+  PredicateExpr.simplify (BasePredicate.applyFlagAssignment flagAssignment)
diff --git a/experiment/configuration/Test/PredicateExpr.hs b/experiment/configuration/Test/PredicateExpr.hs
new file mode 100644
index 0000000000000000000000000000000000000000..25ff0a371f809fe6d75c7bc6506a41bdc5aa090c
--- /dev/null
+++ b/experiment/configuration/Test/PredicateExpr.hs
@@ -0,0 +1,109 @@
+module Test.PredicateExpr where
+
+import PredicateExpr
+import qualified PartialValuation
+import PartialValuation (PartialValuation, TotalValuation)
+
+import Test.PartialValuation
+
+import Test.QuickCheck
+import qualified Test.Laws as Laws
+import Test.Poly (A, B, C, M)
+
+import Text.Show.Functions
+
+import Control.Monad (MonadPlus(..), liftM, liftM2)
+import qualified Data.Foldable as Foldable
+import Data.Maybe (fromMaybe)
+import Data.Monoid (Monoid(mempty))
+
+instance Arbitrary base => Arbitrary (PredicateExpr base) where
+  arbitrary = sized arbExp
+    where
+      arbExp n = frequency $
+        [ (2, liftM PredExprBase arbitrary)
+        , (1, liftM PredExprLit  arbitrary)
+        ] ++ concat
+        [ [ (2, liftM  PredExprNot arbExp1)
+          , (2, liftM2 PredExprAnd arbExp2 arbExp2)
+          , (2, liftM2 PredExprOr  arbExp2 arbExp2)
+          ]
+        | n > 0
+        ]
+        where
+          arbExp1 = arbExp (n-1)
+          arbExp2 = arbExp (n `div` 2)
+
+  shrink (PredExprBase base) = [ PredExprBase base' | base' <- shrink base ]
+  shrink (PredExprLit  lit)  = []
+  shrink (PredExprNot e    ) = e 
+                             : [ PredExprNot e' | e' <- shrink e ]
+  shrink (PredExprAnd e1 e2) = [e1, e2]
+                            ++ [ PredExprAnd e1' e2 | e1' <- shrink e1 ]
+                            ++ [ PredExprAnd e1 e2' | e2' <- shrink e2 ]
+  shrink (PredExprOr  e1 e2) = [e1, e2]
+                            ++ [ PredExprOr e1' e2 | e1' <- shrink e1 ]
+                            ++ [ PredExprOr e1 e2' | e2' <- shrink e2 ]
+
+-- fold is an identity, given suitable constructors as args
+prop_fold_id :: PredicateExpr A -> Bool
+prop_fold_id x = predId x == x
+  where predId = PredicateExpr.fold PredExprBase PredExprLit
+                                    PredExprNot PredExprAnd PredExprOr
+
+-- Check basePredicates against an oracle that's obviously(!) right.
+prop_basePredicates_1 :: PredicateExpr A -> Bool
+prop_basePredicates_1 x = basePredicates x == basePredicates' x 
+  where
+    basePredicates' = PredicateExpr.fold (\x -> [x]) (const []) id (++) (++)
+
+-- PredicateExpr is in Functor, check the functor laws
+prop_fmap_1 :: PredicateExpr A -> Bool
+prop_fmap_1 = Laws.fmap_1
+
+prop_fmap_2 :: (B -> C) -> (A -> B) -> PredicateExpr A -> Bool
+prop_fmap_2 = Laws.fmap_2
+
+-- PredicateExpr is in Foldable, check the laws
+prop_foldable_1 :: PredicateExpr M -> Bool 
+prop_foldable_1 = Laws.foldable_1
+
+prop_foldable_2 :: (A -> B -> B) -> B -> PredicateExpr A -> Bool
+prop_foldable_2 = Laws.foldable_2
+
+-- with a total valuation for the base predicate, eval' is the same as eval
+prop_eval'_1 :: TotalValuation A -> PredicateExpr A -> Bool
+prop_eval'_1 v e = eval' (Just . v) e == Just (eval v e)
+
+-- the overall evaluation of the expression is Nothing if and only if the
+-- base valuation is Nothing on any base value in the expression
+prop_eval'_2 :: (A -> Maybe Bool) -> PredicateExpr A -> Bool
+prop_eval'_2 v e = (eval' v e == Nothing)
+                == any (\e' -> v e' == Nothing) (basePredicates e)
+
+prop_simplify_1 :: PartialValuation A -> TotalValuation A -> PredicateExpr A -> Bool
+prop_simplify_1 v' v e = eval (PartialValuation.extend' v' v) e
+                      == eval v (simplify v' e)
+  where
+    extend f' f = \x -> applyPartialValuation f' x `override` f x
+    override    = flip fromMaybe
+
+prop_simplify_2 :: TotalValuation A -> PredicateExpr A -> Bool
+prop_simplify_2 v e = eval v (simplify mempty e) == eval v e
+
+prop_simplify_3 :: TotalValuation A -> PredicateExpr A -> Bool
+prop_simplify_3 v e = simplify v' e == PredExprLit (eval v e)
+  where
+    v' = PartialValuation.liftTotal v
+
+prop_simplify_4 :: PredicateExpr A -> Bool
+prop_simplify_4 e = not (literalAsSubExpr (simplify PartialValuation.null e))
+  where
+    literalAsSubExpr (PredExprLit _) = False
+    literalAsSubExpr e = PredicateExpr.fold (const False) (const True)
+                                            id (||) (||) e
+-- idempotentcy, at least for a partial valuation that keeps the old expression
+-- when it gives it no value. In other words simplify does all it's work in one
+-- go.
+prop_simplify_5 :: PartialValuation A -> PredicateExpr A -> Bool
+prop_simplify_5 v e = simplify v (simplify v e) == simplify v e