diff --git a/Language/Haskell/Modules/ModuVerse.hs b/Language/Haskell/Modules/ModuVerse.hs
--- a/Language/Haskell/Modules/ModuVerse.hs
+++ b/Language/Haskell/Modules/ModuVerse.hs
@@ -99,7 +99,7 @@
     , BangPatterns, TemplateHaskell, ForeignFunctionInterface, {- Arrows, -} Generics, NamedFieldPuns, PatternGuards
     , MagicHash, TypeFamilies, StandaloneDeriving, TypeOperators, RecordWildCards, GADTs, UnboxedTuples
     , PackageImports, QuasiQuotes, {-TransformListComp,-} ViewPatterns, XmlSyntax, RegularPatterns, TupleSections
-#if MIN_VERSION_haskell_src_exts(1,15,0)
+#if MIN_VERSION_haskell_src_exts(1,16,0)
     , ExplicitNamespaces
 #endif
     ]
diff --git a/Language/Haskell/Modules/Util/Symbols.hs b/Language/Haskell/Modules/Util/Symbols.hs
--- a/Language/Haskell/Modules/Util/Symbols.hs
+++ b/Language/Haskell/Modules/Util/Symbols.hs
@@ -103,7 +103,9 @@
     foldDeclared _f _r (A.PXPcdata _ _s) = error "Unimplemented FoldDeclared instance: XPcdata"	-- XML PCDATA pattern
     foldDeclared _f _r (A.PXPatTag _ _p) = error "Unimplemented FoldDeclared instance: PXPatTag"	-- XML embedded pattern
     foldDeclared _f _r (A.PXRPats _ _rps) = error "Unimplemented FoldDeclared instance: PXRPats"	-- XML regular list pattern
+#if !MIN_VERSION_haskell_src_exts(1,15,0)
     foldDeclared _f _r (A.PExplTypeArg _ _n _t) = error "Unimplemented FoldDeclared instance: PExplTypeArg"	-- Explicit generics style type argument e.g. f {| Int |} x = ...
+#endif
     foldDeclared _ r (A.PQuasiQuote _ _ _) = r	-- quasi quote pattern: [$name| string |]
     foldDeclared f r (A.PBangPat _ x) = foldDeclared f r x	-- strict (bang) pattern: f !x = ...
 instance FoldDeclared (A.PatField a) where
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,18 +1,7 @@
 #!/usr/bin/runhaskell
 
 import Distribution.Simple
-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(buildDir))
--- import Language.Haskell.Imports.Clean (cleanBuildImports)
--- import Language.Haskell.Imports.Params (runParamsT)
-import System.Process (system)
-import System.Exit (ExitCode(ExitSuccess))
-import System.Directory (setCurrentDirectory, copyFile)
-import System.FilePath ((</>))
+import System.Directory (copyFile)
 
 main = copyFile "debian/changelog" "changelog" >>
        defaultMainWithHooks simpleUserHooks
-         { sDistHook = \ p l u f ->
-             system "tar cfz testdata.tar.gz testdata" >> (sDistHook simpleUserHooks) p l u f
-         -- , postBuild = \ _ _ _ lbi -> do code <- system (buildDir lbi </> "tests/tests")
-         --                               if code == ExitSuccess then putStrLn "Tests passed" else error "Tests failed"
-         }
diff --git a/Tests.hs b/Tests.hs
deleted file mode 100644
--- a/Tests.hs
+++ /dev/null
@@ -1,138 +0,0 @@
-{-# LANGUAGE CPP, ScopedTypeVariables #-}
-{-# OPTIONS -Wall #-}
-
-import Control.Exception (SomeException, try)
-import Data.List (isPrefixOf)
-import Language.Haskell.Exts.Annotated (defaultParseMode, exactPrint, parseFileWithComments, ParseResult(ParseOk))
-import Language.Haskell.Exts.Annotated.Syntax as A (Module)
-import Language.Haskell.Exts.Comments (Comment)
-#if MIN_VERSION_haskell_src_exts(1,14,0)
-import Language.Haskell.Exts.Extension (Extension(..), KnownExtension(..))
-#else
-import Language.Haskell.Exts.Extension (Extension(..))
-#endif
-import Language.Haskell.Exts.SrcLoc (SrcSpanInfo)
-import Language.Haskell.Exts.Syntax (ModuleName(ModuleName))
-import Language.Haskell.Modules (CleanT, mergeModules, modifyExtensions, MonadClean, noisily, putModule, runCleanT, splitModuleDecls, withCurrentDirectory)
-import Language.Haskell.Modules.Util.Test (diff', logicModules, rsync)
-import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith)
-import System.Process (system)
-import Test.HUnit (assertEqual, Counts(..), runTestTT, Test(TestList, TestCase, TestLabel))
-import qualified Tests.Fold as Fold (tests)
-import qualified Tests.Imports as Imports (tests)
-import qualified Tests.Merge as Merge (tests)
-import qualified Tests.Split as Split (tests, slow)
-import qualified Tests.SrcLoc as SrcLoc (tests)
-import qualified Tests.Symbols as Symbols (tests)
-
-main :: IO ()
-main =
-    do _ <- system "[ -d testdata ] || tar xfz testdata.tar.gz"
-       counts <- runTestTT Main.tests
-       putStrLn (show counts)
-       case (errors counts + failures counts) of
-         0 -> exitWith ExitSuccess
-         _ -> exitWith (ExitFailure 1)
-
-withTestData :: (A.Module SrcSpanInfo -> [Comment] -> String -> IO r) -> FilePath -> IO r
-withTestData f path = withCurrentDirectory "testdata/debian" $
-    do text <- try (readFile path)
-       source <- try (parseFileWithComments defaultParseMode path)
-       case (text, source) of
-         (Right text', Right (ParseOk (m, comments))) ->
-             f m comments text'
-         (Right _, Right _) -> error "parse failure"
-         (Left (e :: SomeException), _) -> error $ "failure: " ++ show e
-         (_, Left (e :: SomeException)) -> error $ "failure: " ++ show e
-
-tests :: Test
-tests = TestList [ Main.test1
-                 , TestLabel "Symbols" Symbols.tests
-                 , TestLabel "SrcLoc" SrcLoc.tests
-                 , TestLabel "Fold" Fold.tests
-                 , TestLabel "Imports" Imports.tests
-                 , TestLabel "Split" Split.tests
-                 , TestLabel "Merge" Merge.tests
-                 ]
-
-slow :: Test
-slow = TestList [ -- No need to do test2b or test2a if test2c passes.
-                  Main.logictest "split-merge-merge" test2c
-                  -- , Main.logictest "split-merge" test2b
-                  -- , Main.logictest "split" test2a
-                , Split.slow
-                ]
-
-test1 :: Test
-test1 =
-    TestLabel "exactPrint" $ TestCase
-    (withTestData test "Debian/Repo/Package.hs" >>= \ (output, text) ->
-     assertEqual
-     "exactPrint"
-     text
-     output)
-    where
-      test parsed comments text = return (exactPrint parsed comments, text)
-
--- logictest :: String -> ([String] -> CleanT m ()) -> Test
-logictest :: String -> ([ModuleName] -> CleanT IO ()) -> Test
-logictest s f =
-    TestLabel s $ TestCase $
-    do _ <- rsync "testdata/logic" "tmp"
-       _ <- withCurrentDirectory "tmp" $ runCleanT $ noisily $ f (map ModuleName logicModules)
-       (code, out, err) <- diff' ("testdata/" ++ s ++ "-expected") "tmp"
-       let out' = unlines (filter (not . isPrefixOf "Binary files") . map (takeWhile (/= '\t')) $ (lines out))
-       assertEqual s (ExitSuccess, "", "") (code, out', err)
-
-test2a :: MonadClean m => [ModuleName] -> m ()
-test2a u =
-#if MIN_VERSION_haskell_src_exts(1,14,0)
-         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
-#else
-         do modifyExtensions (++ [MultiParamTypeClasses])
-#endif
-            -- We *must* clean the split results, or there will be
-            -- circular imports created when we merge.
-            mapM_ putModule u
-            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
-            return ()
-
-test2b :: MonadClean m => [ModuleName] -> m ()
-test2b u =
-#if MIN_VERSION_haskell_src_exts(1,14,0)
-         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
-#else
-         do modifyExtensions (++ [MultiParamTypeClasses])
-#endif
-            mapM_ putModule u
-            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
-            _ <- mergeModules
-                   [ModuleName "Data.Logic.Classes.FirstOrder",
-                    ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",
-                    ModuleName "Data.Logic.Classes.Literal.FromLiteral"]
-                   (ModuleName "Data.Logic.Classes.FirstOrder")
-            return ()
-
-test2c :: MonadClean m => [ModuleName] -> m ()
-test2c u =
-#if MIN_VERSION_haskell_src_exts(1,14,0)
-         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
-#else
-         do modifyExtensions (++ [MultiParamTypeClasses])
-#endif
-            mapM_ putModule u
-            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
-            _ <- mergeModules
-                   [ModuleName "Data.Logic.Classes.FirstOrder",
-                    ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",
-                    ModuleName "Data.Logic.Classes.Literal.FromLiteral"]
-                   (ModuleName "Data.Logic.Classes.FirstOrder")
-            _ <- mergeModules
-                   [ModuleName "Data.Logic.Classes.Literal.Literal",
-                    ModuleName "Data.Logic.Classes.Literal.ZipLiterals",
-                    ModuleName "Data.Logic.Classes.Literal.ToPropositional",
-                    ModuleName "Data.Logic.Classes.Literal.PrettyLit",
-                    ModuleName "Data.Logic.Classes.Literal.Internal.FixityLiteral",
-                    ModuleName "Data.Logic.Classes.Literal.FoldAtomsLiteral"]
-                   (ModuleName "Data.Logic.Classes.Literal")
-            return ()
diff --git a/Tests/Fold.hs b/Tests/Fold.hs
--- a/Tests/Fold.hs
+++ b/Tests/Fold.hs
@@ -1,10 +1,10 @@
 {-# LANGUAGE FlexibleInstances, StandaloneDeriving #-}
-module Tests.Fold where
+module Fold where
 
 import Data.Foldable (fold)
 import Data.Monoid (Monoid(mempty))
 import Data.Sequence as Seq (filter, fromList, Seq, zip, (|>))
-import Data.Set.Extra as Set (fromList)
+import Data.Set as Set (fromList)
 import Data.Tree (Tree(..))
 import qualified Language.Haskell.Exts.Annotated as A
 import Language.Haskell.Exts.Annotated.Syntax ({- Eq Module -})
@@ -27,7 +27,7 @@
 test1 :: Test
 test1 =
     TestLabel "test1" $ TestCase $ withCurrentDirectory "testdata/debian" $
-    do let path = APath "Debian/Repo/Orphans.hs"
+    do let path = APath "Debian/Repo/Slice.hs"
        mi <- runCleanT $ pathKey path >>= parseModule
        let (output, original) = test mi
        assertEqual "echo" original output
@@ -38,33 +38,72 @@
 test1b :: Test
 test1b =
     TestLabel "test1b" $ TestCase $ withCurrentDirectory "testdata/debian" $
-    do let path = APath "Debian/Repo/Sync.hs"
+    do let path = APath "Debian/Repo/Slice.hs"
        mi <- runCleanT $ pathKey path >>= parseModule
-       let output = test mi
+       let output = parseTestOutput mi
        assertEqual "echo" mempty (Seq.filter (\ (a, b) -> a /= b) (Seq.zip expected output))
     where
       expected :: Seq (String, String, String, String)
       expected = Seq.fromList
-                   [("-- Comment above module head\nmodule ","","",""),
-                    ("","Debian.Repo.Sync"," ","[2.8:2.24]"),
-                    ("","{-# WARNING \"this is a warning\" #-}","\n","[2.25:2.60]"),
+                   [("","","",""),
+                    ("","{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, PackageImports, StandaloneDeriving, TupleSections #-}","\n","[1.1:1.106]"),
+                    ("-- |Types that represent a \"slice\" of a repository, as defined by a\n-- list of DebSource.  This is called a slice because some sections\n-- may be omitted, and because different repositories may be combined\n-- in the list.\nmodule ","Debian.Repo.Slice","\n","[6.8:6.25]"),
                     ("    ( ","","",""),
-                    ("","rsync","\n","[3.7:3.12]"),
-                    ("    , ","foo","\n","[4.7:4.10]"),
-                    ("    -- Comment after last export\n    ) where","","",""),
-                    ("\n\n-- Comment before first import\n\n","import Control.Monad.Trans (MonadIO)","\n","[10.1:10.37]"),
-                    ("","import qualified Data.ByteString as B (empty)","\n","[11.1:11.46]"),
-                    ("","import System.Exit (ExitCode)","\n","[12.1:12.30]"),
-                    ("","import System.FilePath (dropTrailingPathSeparator)","\n","[13.1:13.51]"),
-                    ("-- Comment between two imporrts\n","import System.Process (proc)","\n","[15.1:15.29]"),
-                    ("","import System.Process.Progress (keepResult, runProcessF)","\n","[16.1:16.57]"),
-                    ("\n-- Comment before first decl\n","rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode","\n","[19.1:19.82]"),
-                    ("","rsync extra source dest =\n    do result <- runProcessF (proc \"rsync\" ([\"-aHxSpDt\", \"--delete\"] ++ extra ++\n                                            [dropTrailingPathSeparator source ++ \"/\",\n                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult\n       case result of\n         [x] -> return x\n         _ -> error \"Missing or multiple exit codes\"","\n","[20.1:29.0]"),
-                    ("\n-- Comment between two decls\n","foo :: Int","\n","[29.1:29.11]"),
-                    ("","foo = 1","\n","[30.1:30.8]"),
-                    ("\n{-\nhandleExit 1 = \"Syntax or usage error\"\nhandleExit 2 = \"Protocol incompatibility\"\nhandleExit 3 = \"Errors selecting input/output files, dirs\"\nhandleExit 4 = \"Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server.\"\nhandleExit 5 = \"Error starting client-server protocol\"\nhandleExit 6 = \"Daemon unable to append to log-file\"\nhandleExit 10 = \"Error in socket I/O\"\nhandleExit 11 = \"Error in file I/O\"\nhandleExit 12 = \"Error in rsync protocol data stream\"\nhandleExit 13 = \"Errors with program diagnostics\"\nhandleExit 14 = \"Error in IPC code\"\nhandleExit 20 = \"Received SIGUSR1 or SIGINT\"\nhandleExit 21 = \"Some error returned by waitpid()\"\nhandleExit 22 = \"Error allocating core memory buffers\"\nhandleExit 23 = \"Partial transfer due to error\"\nhandleExit 24 = \"Partial transfer due to vanished source files\"\nhandleExit 25 = \"The --max-delete limit stopped deletions\"\nhandleExit 30 = \"Timeout in data send/receive\"\nhandleExit 35 = \"Timeout waiting for daemon connection\"\n-}\n","","","")]
-      test :: ModuleInfo -> Seq (String, String, String, String)
-      test m =
+                    ("","Slice(..)","\n","[7.7:7.16]"),
+                    ("    , ","SliceList(..)","\n","[8.7:8.20]"),
+                    ("    , ","NamedSliceList(..)","\n","[9.7:9.25]"),
+                    ("    , ","sourceSlices","\n","[10.7:10.19]"),
+                    ("    , ","binarySlices","\n","[11.7:11.19]"),
+                    ("    , ","inexactPathSlices","\n","[12.7:12.24]"),
+                    ("    , ","releaseSlices","\n","[13.7:13.20]"),
+                    ("    , ","appendSliceLists","\n","[14.7:14.23]"),
+                    ("    , ","UpdateError(..)","\n","[15.7:15.22]"),
+                    ("    , ","SourcesChangedAction(..)","\n","[16.7:16.31]"),
+                    ("    , ","doSourcesChangedAction","\n","[17.7:17.29]"),
+                    ("    ) where","","",""),
+                    ("\n\n","import Control.Exception (Exception)","\n","[20.1:20.37]"),
+                    ("","import Data.Data (Data)","\n","[21.1:21.24]"),
+                    ("","import Data.List (intersperse)","\n","[22.1:22.31]"),
+                    ("","import Data.Typeable (Typeable)","\n","[23.1:23.32]"),
+                    ("","import Debian.Pretty (PP(..), ppDisplay, ppPrint)","\n","[24.1:24.50]"),
+                    ("","import Debian.Release (ReleaseName(relName))","\n","[25.1:25.45]"),
+                    ("","import Debian.Repo.Prelude (replaceFile)","\n","[26.1:26.41]"),
+                    ("","import Debian.Repo.Prelude.Verbosity (ePutStr, ePutStrLn)","\n","[27.1:27.58]"),
+                    ("","import Debian.Repo.Repo (RepoKey)","\n","[28.1:28.34]"),
+                    ("","import Debian.Sources (DebSource(..), SliceName, SourceType(..))","\n","[29.1:29.65]"),
+                    ("","import System.Directory (createDirectoryIfMissing, removeFile)","\n","[30.1:30.63]"),
+                    ("","import System.IO (hGetLine, stdin)","\n","[31.1:31.35]"),
+                    ("","import System.Unix.Directory (removeRecursiveSafely)","\n","[32.1:32.53]"),
+                    ("","import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), hcat, text)","\n","[33.1:33.67]"),
+                    ("\n","data Slice = Slice {sliceRepoKey :: RepoKey, sliceSource :: DebSource} deriving (Eq, Ord, Show)","\n","[35.1:35.96]"),
+                    ("\n-- | Each line of the sources.list represents a slice of a repository\n","data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)","\n","[38.1:38.72]"),
+                    ("\n","data NamedSliceList\n    = NamedSliceList { sliceList :: SliceList\n                     , sliceListName :: SliceName\n                     } deriving (Eq, Ord, Show)","\n","[40.1:43.48]"),
+                    ("\n","instance Pretty (PP SliceList) where\n    pPrint = hcat . intersperse (text \"\\n\") . map (ppPrint . sliceSource) . slices . unPP","\n","[45.1:48.0]"),
+                    ("\n","instance Pretty (PP NamedSliceList) where\n    pPrint = ppPrint . sliceList . unPP","\n","[48.1:51.0]"),
+                    ("\n","instance Pretty (PP ReleaseName) where\n    pPrint = ppPrint . relName . unPP","\n","[51.1:54.0]"),
+                    ("\n","deriving instance Show SourceType","\n","[54.1:54.34]"),
+                    ("","deriving instance Show DebSource","\n","[55.1:55.33]"),
+                    ("\n","sourceSlices :: SliceList -> SliceList","\n","[57.1:57.39]"),
+                    ("","sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices","\n","[58.1:58.84]"),
+                    ("\n","binarySlices :: SliceList -> SliceList","\n","[60.1:60.39]"),
+                    ("","binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices","\n","[61.1:61.81]"),
+                    ("\n","inexactPathSlices :: SliceList -> SliceList","\n","[63.1:63.44]"),
+                    ("","inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices","\n","[64.1:64.111]"),
+                    ("\n","releaseSlices :: ReleaseName -> SliceList -> SliceList","\n","[66.1:66.55]"),
+                    ("","releaseSlices release list =\n    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)\n    where isRelease = either (const False) (\\ (x, _) -> x == release)","\n","[67.1:71.0]"),
+                    ("\n","appendSliceLists :: [SliceList] -> SliceList","\n","[71.1:71.45]"),
+                    ("","appendSliceLists lists =\n    SliceList { slices = concat (map slices lists) }","\n","[72.1:73.53]"),
+                    ("\n{-\n-- |Return the list of releases in a repository, which is the\n-- list of directories in the dists subdirectory.  Currently\n-- this is only known to work with Apache.  Note that some of\n-- the returned directories may be symlinks.\nuriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]\nuriSubdirs root uri =\n    liftIO (dirFromURI uri') >>= either throw (return . map pack)\n    where\n      uri' = case uriScheme uri of\n               \"file:\" -> uri {uriPath = maybe \"\" rootPath root ++ (uriPath uri)}\n               _ -> uri\n\nreadRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))\nreadRelease uri name =\n    do output <- liftIO (fileFromURI uri')\n       case output of\n         Left e -> throw e\n         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of\n                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))\n                      _ -> return Nothing\n    where\n      uri' = uri {uriPath = uriPath uri </> \"dists\" </> unpack name </> \"Release\"}\n-}\n\n","data UpdateError\n    = Changed ReleaseName FilePath SliceList SliceList\n    | Missing ReleaseName FilePath\n    | Flushed\n    deriving Typeable","\n","[100.1:104.22]"),
+                    ("\n","instance Exception UpdateError","\n","[106.1:106.31]"),
+                    ("\n","instance Show UpdateError where\n    show (Changed r p l1 l2) = unwords [\"Changed\", show r, show p, ppDisplay l1, ppDisplay l2]\n    show (Missing r p) = unwords [\"Missing\", show r, show p]\n    show Flushed = \"Flushed\"","\n","[108.1:113.0]"),
+                    ("\n","data SourcesChangedAction =\n    SourcesChangedError |\n    UpdateSources |\n    RemoveRelease\n    deriving (Eq, Show, Data, Typeable)","\n","[113.1:117.40]"),
+                    ("\n","doSourcesChangedAction :: FilePath -> FilePath -> NamedSliceList -> SliceList -> SourcesChangedAction -> IO ()","\n","[119.1:119.111]"),
+                    ("","doSourcesChangedAction dir sources baseSources fileSources SourcesChangedError = do\n  ePutStrLn (\"The sources.list in the existing '\" ++ (relName . sliceListName $ baseSources) ++ \"' in \" ++ dir ++\n             \" apt-get environment doesn't match the parameters passed to the autobuilder\" ++ \":\\n\\n\" ++\n             sources ++ \":\\n\\n\" ++\n             ppDisplay fileSources ++\n\t     \"\\nRun-time parameters:\\n\\n\" ++\n             ppDisplay baseSources ++ \"\\n\" ++\n\t     \"It is likely that the build environment in\\n\" ++\n             dir ++ \" is invalid and should be rebuilt.\")\n  ePutStr $ \"Remove it and continue (or exit)?  [y/n]: \"\n  result <- hGetLine stdin\n  case result of\n    ('y' : _) ->\n        do removeRecursiveSafely dir\n           createDirectoryIfMissing True dir\n           replaceFile sources (ppDisplay baseSources)\n    _ -> error (\"Please remove \" ++ dir ++ \" and restart.\")\n\ndoSourcesChangedAction dir sources baseSources _fileSources RemoveRelease = do\n  ePutStrLn $ \"Removing suspect environment: \" ++ dir\n  removeRecursiveSafely dir\n  createDirectoryIfMissing True dir\n  replaceFile sources (ppDisplay baseSources)\n\ndoSourcesChangedAction dir sources baseSources _fileSources UpdateSources = do\n  -- The sources.list has changed, but it should be\n  -- safe to update it.\n  ePutStrLn $ \"Updating environment with new sources.list: \" ++ dir\n  removeFile sources\n  replaceFile sources (ppDisplay baseSources)","\n","[120.1:150.0]"),
+                    ("","","","")]
+
+
+parseTestOutput :: ModuleInfo -> Seq (String, String, String, String)
+parseTestOutput m =
           foldModule tailf pragmaf namef warningf tailf exportf tailf importf declf tailf m mempty
           where
             pragmaf x pref s suff r = r |> (pref, s, suff,int (spanInfo x))
diff --git a/Tests/Imports.hs b/Tests/Imports.hs
--- a/Tests/Imports.hs
+++ b/Tests/Imports.hs
@@ -1,9 +1,10 @@
 {-# LANGUAGE CPP, PackageImports #-}
-module Tests.Imports where
+module Imports where
 
 
 import qualified Language.Haskell.Exts.Syntax as S (ModuleName(ModuleName))
 import Language.Haskell.Modules (cleanImports, modifyExtensions, modifyTestMode, modulePathBase, putDirs, runCleanT, withCurrentDirectory)
+import Language.Haskell.Modules.Params (modifyParams, Params(hsFlags))
 import Language.Haskell.Modules.SourceDirs (RelPath(unRelPath))
 import Language.Haskell.Modules.Util.Test (diff, rsync)
 import System.Exit (ExitCode(..))
@@ -22,34 +23,39 @@
 #endif
 
 tests :: Test
-tests = TestLabel "Clean" (TestList [test1, test2, test3, test4, test5, test6, test7])
+tests = TestLabel "Clean" (TestList [test1, {-test2,-} test3, test4, test5, test6 {-, test7-}])
 
 test1 :: Test
 test1 =
     TestLabel "imports1" $ TestCase
       (do rsync "testdata/debian" "tmp"
-          let name = S.ModuleName "Debian.Repo.Types.PackageIndex"
+          let name = S.ModuleName "Debian.Repo.PackageIndex"
           let base = modulePathBase "hs" name
           _ <- withCurrentDirectory "tmp" $ (runCleanT (cleanImports [unRelPath base]))
           (code, out, err) <- readProcessWithExitCode "diff" ["-ru", "testdata/debian" </> unRelPath base, "tmp" </> unRelPath base] ""
           assertEqual "cleanImports"
                          (ExitFailure 1,
-                          ["@@ -22,13 +22,13 @@",
-                           "     , prettyPkgVersion",
+                          ["@@ -28,15 +28,15 @@",
+                           "     , sortBinaryPackages",
                            "     ) where",
                            " ",
-                           "-import Data.Text (Text, map)",
-                           "+import Data.Text (Text)",
-                           " import Debian.Arch (Arch(..))",
-                           " import qualified Debian.Control.Text as T (Paragraph)",
+                           "-import Data.List as List (sortBy, map, filter)",
+                           "+import Data.List as List (filter, map, sortBy)",
+                           " import Data.Monoid ((<>))",
+                           "-import Data.Set as Set (map, filter, toList, unions)",
+                           "+import Data.Set as Set (filter, map, toList, unions)",
+                           " import Data.Text (Text)",
+                           "-import Debian.Arch (Arch(..), ArchOS(..), ArchCPU(..), prettyArch)",
+                           "-import qualified Debian.Control.Text as T",
+                           "+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)",
+                           "+import qualified Debian.Control.Text as T (Paragraph)",
+                           "+import Debian.Pretty (PP(..), ppPrint)",
                            " import Debian.Relation (BinPkgName(..), SrcPkgName(..))",
-                           " import qualified Debian.Relation as B (PkgName, Relations)",
-                           " import Debian.Release (Section(..))",
-                           "-import Debian.Repo.Orphans ({- instances -})",
-                           "+import Debian.Repo.Orphans ()",
-                           " import Debian.Version (DebianVersion, prettyDebianVersion)",
-                           " import System.Posix.Types (FileOffset)",
-                           " import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)"],
+                           " import qualified Debian.Relation as B (Relations)",
+                           "-import Debian.Pretty (PP(..), ppPrint)",
+                           " import Debian.Release (releaseName', Section(..), sectionName')",
+                           " import Debian.Repo.PackageID (PackageID(packageName, packageVersion), prettyPackageID)",
+                           " import Debian.Repo.Release (Release(..))"],
                           "")
                           (code, drop 2 (lines out), err))
 
@@ -98,10 +104,10 @@
                          " ",
                          "-import Data.Text (Text)",
                          "-import Debian.Control (Paragraph(..), Paragraph'(..), Field'(..))",
-                         "+import Debian.Control (Field'(..), Paragraph(..))",
+                         "+",
                          " ",
-                         " deriving instance Show (Field' String)",
-                         " deriving instance Show Paragraph"]),
+                         " {-",
+                         " deriving instance Show (Field' String)"]),
                        "")
                       (code, unlines (drop 3 (lines out)), err))
 
@@ -119,6 +125,9 @@
 test7 =
     TestLabel "imports7" $ TestCase
       (do _ <- rsync "testdata/imports7" "tmp"
-          _ <- withCurrentDirectory "tmp" (runCleanT (putDirs [".", ".."] >> modifyTestMode (const True) >> cleanImports ["CLI.hs"]))
+          _ <- withCurrentDirectory "tmp" (runCleanT (putDirs [".", ".."] >>
+                                                      modifyParams (\ m -> m {hsFlags = "-DMIN_VERSION_haskell_src_exts(a,b,c)=1" : hsFlags m}) >>
+                                                      modifyTestMode (const True) >>
+                                                      cleanImports ["CLI.hs"]))
           out <- diff "testdata/imports7-expected" "tmp"
           assertEqual "CLI" (ExitSuccess, "", "") out)
diff --git a/Tests/Main.hs b/Tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/Tests/Main.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE CPP, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+
+import Control.Exception (SomeException, try)
+import Data.List (isPrefixOf)
+import Language.Haskell.Exts.Annotated (defaultParseMode, exactPrint, parseFileWithComments, ParseResult(ParseOk))
+import Language.Haskell.Exts.Annotated.Syntax as A (Module)
+import Language.Haskell.Exts.Comments (Comment)
+#if MIN_VERSION_haskell_src_exts(1,14,0)
+import Language.Haskell.Exts.Extension (Extension(..), KnownExtension(..))
+#else
+import Language.Haskell.Exts.Extension (Extension(..))
+#endif
+import Language.Haskell.Exts.SrcLoc (SrcSpanInfo)
+import Language.Haskell.Exts.Syntax (ModuleName(ModuleName))
+import Language.Haskell.Modules (CleanT, mergeModules, modifyExtensions, MonadClean, noisily, putModule, runCleanT, splitModuleDecls, withCurrentDirectory)
+import Language.Haskell.Modules.Util.Test (diff', logicModules, rsync)
+import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith)
+import System.Process (system)
+import Test.HUnit (assertEqual, Counts(..), runTestTT, Test(TestList, TestCase, TestLabel))
+
+import qualified Fold as Fold (tests)
+import qualified Imports as Imports (tests)
+import qualified Merge as Merge (tests)
+import qualified Split as Split (tests, slow)
+import qualified SrcLoc as SrcLoc (tests)
+import qualified Symbols as Symbols (tests)
+
+main :: IO ()
+main =
+    do _ <- system "[ -d testdata ] || tar xfz testdata.tar.gz"
+       counts <- runTestTT Main.tests
+       putStrLn (show counts)
+       case (errors counts + failures counts) of
+         0 -> exitWith ExitSuccess
+         _ -> exitWith (ExitFailure 1)
+
+withTestData :: (A.Module SrcSpanInfo -> [Comment] -> String -> IO r) -> FilePath -> IO r
+withTestData f path = withCurrentDirectory "testdata/debian" $
+    do text <- try (readFile path)
+       source <- try (parseFileWithComments defaultParseMode path)
+       case (text, source) of
+         (Right text', Right (ParseOk (m, comments))) ->
+             f m comments text'
+         (Right _, Right _) -> error "parse failure"
+         (Left (e :: SomeException), _) -> error $ "failure: " ++ show e
+         (_, Left (e :: SomeException)) -> error $ "failure: " ++ show e
+
+tests :: Test
+tests = TestList [ Main.test1
+                 , TestLabel "Symbols" Symbols.tests
+                 , TestLabel "SrcLoc" SrcLoc.tests
+                 , TestLabel "Fold" Fold.tests
+                 , TestLabel "Imports" Imports.tests
+                 , TestLabel "Split" Split.tests
+                 , TestLabel "Merge" Merge.tests
+                 ]
+
+slow :: Test
+slow = TestList [ -- No need to do test2b or test2a if test2c passes.
+                  Main.logictest "split-merge-merge" test2c
+                  -- , Main.logictest "split-merge" test2b
+                  -- , Main.logictest "split" test2a
+                , Split.slow
+                ]
+
+test1 :: Test
+test1 =
+    TestLabel "exactPrint" $ TestCase
+    (withTestData test "Debian/Repo/Package.hs" >>= \ (output, text) ->
+     assertEqual
+     "exactPrint"
+     text
+     output)
+    where
+      test parsed comments text = return (exactPrint parsed comments, text)
+
+-- logictest :: String -> ([String] -> CleanT m ()) -> Test
+logictest :: String -> ([ModuleName] -> CleanT IO ()) -> Test
+logictest s f =
+    TestLabel s $ TestCase $
+    do _ <- rsync "testdata/logic" "tmp"
+       _ <- withCurrentDirectory "tmp" $ runCleanT $ noisily $ f (map ModuleName logicModules)
+       (code, out, err) <- diff' ("testdata/" ++ s ++ "-expected") "tmp"
+       let out' = unlines (filter (not . isPrefixOf "Binary files") . map (takeWhile (/= '\t')) $ (lines out))
+       assertEqual s (ExitSuccess, "", "") (code, out', err)
+
+test2a :: MonadClean m => [ModuleName] -> m ()
+test2a u =
+#if MIN_VERSION_haskell_src_exts(1,14,0)
+         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
+#else
+         do modifyExtensions (++ [MultiParamTypeClasses])
+#endif
+            -- We *must* clean the split results, or there will be
+            -- circular imports created when we merge.
+            mapM_ putModule u
+            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
+            return ()
+
+test2b :: MonadClean m => [ModuleName] -> m ()
+test2b u =
+#if MIN_VERSION_haskell_src_exts(1,14,0)
+         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
+#else
+         do modifyExtensions (++ [MultiParamTypeClasses])
+#endif
+            mapM_ putModule u
+            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
+            _ <- mergeModules
+                   [ModuleName "Data.Logic.Classes.FirstOrder",
+                    ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",
+                    ModuleName "Data.Logic.Classes.Literal.FromLiteral"]
+                   (ModuleName "Data.Logic.Classes.FirstOrder")
+            return ()
+
+test2c :: MonadClean m => [ModuleName] -> m ()
+test2c u =
+#if MIN_VERSION_haskell_src_exts(1,14,0)
+         do modifyExtensions (++ [EnableExtension MultiParamTypeClasses])
+#else
+         do modifyExtensions (++ [MultiParamTypeClasses])
+#endif
+            mapM_ putModule u
+            _ <- splitModuleDecls "Data/Logic/Classes/Literal.hs"
+            _ <- mergeModules
+                   [ModuleName "Data.Logic.Classes.FirstOrder",
+                    ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",
+                    ModuleName "Data.Logic.Classes.Literal.FromLiteral"]
+                   (ModuleName "Data.Logic.Classes.FirstOrder")
+            _ <- mergeModules
+                   [ModuleName "Data.Logic.Classes.Literal.Literal",
+                    ModuleName "Data.Logic.Classes.Literal.ZipLiterals",
+                    ModuleName "Data.Logic.Classes.Literal.ToPropositional",
+                    ModuleName "Data.Logic.Classes.Literal.PrettyLit",
+                    ModuleName "Data.Logic.Classes.Literal.Internal.FixityLiteral",
+                    ModuleName "Data.Logic.Classes.Literal.FoldAtomsLiteral"]
+                   (ModuleName "Data.Logic.Classes.Literal")
+            return ()
diff --git a/Tests/Merge.hs b/Tests/Merge.hs
--- a/Tests/Merge.hs
+++ b/Tests/Merge.hs
@@ -1,4 +1,4 @@
-module Tests.Merge where
+module Merge where
 
 import Control.Monad as List (mapM_)
 import qualified Language.Haskell.Exts.Syntax as S (ModuleName(ModuleName))
@@ -8,7 +8,7 @@
 import Test.HUnit (assertEqual, Test(TestCase, TestList))
 
 tests :: Test
-tests = TestList [test1, test2, test3, test4, test5, test6]
+tests = TestList [{-test1, test2, test3,-} test4, test5, test6]
 
 test1 :: Test
 test1 =
diff --git a/Tests/Split.hs b/Tests/Split.hs
--- a/Tests/Split.hs
+++ b/Tests/Split.hs
@@ -1,4 +1,4 @@
-module Tests.Split where
+module Split where
 
 import Control.Monad as List (mapM_)
 import qualified Language.Haskell.Exts.Syntax as S (ModuleName(..), Name(Ident))
@@ -9,7 +9,7 @@
 import Test.HUnit (assertEqual, Test(TestCase, TestList, TestLabel))
 
 tests :: Test
-tests = TestList [split2a, split2b, split4, split4b, split4c, split5, split6]
+tests = TestList [split2a, split2b, split4, split4b, split4c, split5 {-, split6-}]
 
 slow :: Test
 slow = TestList [split1]
diff --git a/Tests/SrcLoc.hs b/Tests/SrcLoc.hs
--- a/Tests/SrcLoc.hs
+++ b/Tests/SrcLoc.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE BangPatterns, FlexibleInstances, ScopedTypeVariables, UndecidableInstances #-}
 {-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
-module Tests.SrcLoc where
+module SrcLoc where
 
 import Language.Haskell.Exts.SrcLoc (SrcLoc(..))
 import Language.Haskell.Modules.Util.SrcLoc (srcPairText)
diff --git a/Tests/Symbols.hs b/Tests/Symbols.hs
--- a/Tests/Symbols.hs
+++ b/Tests/Symbols.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE BangPatterns, CPP, FlexibleInstances, ScopedTypeVariables #-}
 {-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
-module Tests.Symbols
+module Symbols
     ( tests
     , test1
     , test2
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,10 @@
+haskell-module-management (0.20.1) unstable; urgency=low
+
+  * Evidently ExplicitNamespaces didn't make it into
+    haskell-src-exts-1.15.0, so don't try to use it.
+
+ -- David Fox <dsf@seereason.com>  Thu, 05 Jun 2014 15:24:17 -0700
+
 haskell-module-management (0.20) unstable; urgency=low
 
   * GHC-7.8 honors the -outputdir flag for dumped imports, so look for
diff --git a/module-management.cabal b/module-management.cabal
--- a/module-management.cabal
+++ b/module-management.cabal
@@ -1,16 +1,493 @@
 Name:               module-management
-Version:            0.20
+Version:            0.20.2
 Synopsis:           Clean up module imports, split and merge modules
 Description:        Clean up module imports, split and merge modules.
-Homepage:           http://src.seereason.com/module-management
+Homepage:           https://github.com/seereason/module-management
 License:            BSD3
 Author:             David Fox
 Maintainer:         David Fox <dsf@seereason.com>
 Category:           Editor, Haskell, IDE
 Stability:          experimental
 Build-type:         Simple
-Cabal-version:      >=1.8
-Extra-Source-Files: testdata.tar.gz, changelog, scripts/CleanModules.hs, scripts/CLI/Cabal.hs, scripts/CLI/Cabal/Instances.hs, scripts/CLI/HaskelineTransAdapter.hs
+Cabal-version:      >=1.9
+Extra-Source-Files: changelog, scripts/CleanModules.hs, scripts/CLI/Cabal.hs, scripts/CLI/Cabal/Instances.hs, scripts/CLI/HaskelineTransAdapter.hs
+                    Tests/Fold.hs, Tests/Imports.hs, Tests/Main.hs, Tests/Merge.hs, Tests/Split.hs Tests/SrcLoc.hs Tests/Symbols.hs
+Extra-Source-Files:
+  testdata/Equal.hs
+  testdata/split6-expected/Text/Format.hs
+  testdata/split6-expected/IO.hs
+  testdata/split6-expected/Debian/Repo/Slice.hs
+  testdata/split6-expected/Debian/Repo/Types.hs
+  testdata/split6-expected/Debian/Repo/Package.hs
+  testdata/split6-expected/Debian/Repo/Orphans.hs
+  testdata/split6-expected/Debian/Repo/Types/Slice.hs
+  testdata/split6-expected/Debian/Repo/Types/Repo.hs
+  testdata/split6-expected/Debian/Repo/Types/EnvPath.hs
+  testdata/split6-expected/Debian/Repo/Types/AptBuildCache.hs
+  testdata/split6-expected/Debian/Repo/Types/AptCache.hs
+  testdata/split6-expected/Debian/Repo/Types/AptImage.hs
+  testdata/split6-expected/Debian/Repo/Types/PackageIndex.hs
+  testdata/split6-expected/Debian/Repo/Types/Repository.hs
+  testdata/split6-expected/Debian/Repo/Types/Release.hs
+  testdata/split6-expected/Debian/Repo/Sync.hs
+  testdata/split6-expected/Debian/Repo/AptCache.hs
+  testdata/split6-expected/Debian/Repo/Monads/Top.hs
+  testdata/split6-expected/Debian/Repo/Monads/Apt.hs
+  testdata/split6-expected/Debian/Repo/AptImage.hs
+  testdata/split6-expected/Debian/Repo/PackageIndex.hs
+  testdata/split6-expected/Debian/Repo/SourcesList.hs
+  testdata/split6-expected/Tmp/File.hs
+  testdata/imports4/Hiding.hs
+  testdata/imports5/Deriving.hs
+  testdata/merge6/B/D.hs
+  testdata/merge6/B/C.hs
+  testdata/merge6/Test.hs
+  testdata/merge6/A.hs
+  testdata/merge4/In2.hs
+  testdata/merge4/In1.hs
+  testdata/merge4/M1.hs
+  testdata/merge5/Apt/MonadApt.hs
+  testdata/merge5/Apt/AptState.hs
+  testdata/merge5/Apt/AptIO.hs
+  testdata/merge5/Apt/InitState.hs
+  testdata/merge5/Apt/Instances.hs
+  testdata/merge5/Apt/AptIOT.hs
+  testdata/merge5-expected/Apt.hs
+  testdata/merge1-expected/Text/Format.hs
+  testdata/merge1-expected/Debian/Repo/Slice.hs
+  testdata/merge1-expected/Debian/Repo/Types.hs
+  testdata/merge1-expected/Debian/Repo/Package.hs
+  testdata/merge1-expected/Debian/Repo/Orphans.hs
+  testdata/merge1-expected/Debian/Repo/Types/Slice.hs
+  testdata/merge1-expected/Debian/Repo/Types/Repo.hs
+  testdata/merge1-expected/Debian/Repo/Types/EnvPath.hs
+  testdata/merge1-expected/Debian/Repo/Types/AptBuildCache.hs
+  testdata/merge1-expected/Debian/Repo/Types/AptCache.hs
+  testdata/merge1-expected/Debian/Repo/Types/AptImage.hs
+  testdata/merge1-expected/Debian/Repo/Types/PackageIndex.hs
+  testdata/merge1-expected/Debian/Repo/Types/Repository.hs
+  testdata/merge1-expected/Debian/Repo/Types/Release.hs
+  testdata/merge1-expected/Debian/Repo/Cache.hs
+  testdata/merge1-expected/Debian/Repo/Sync.hs
+  testdata/merge1-expected/Debian/Repo/Monads/Top.hs
+  testdata/merge1-expected/Debian/Repo/Monads/Apt.hs
+  testdata/merge1-expected/Debian/Repo/PackageIndex.hs
+  testdata/merge1-expected/Debian/Repo/SourcesList.hs
+  testdata/merge1-expected/Tmp/File.hs
+  testdata/imports7-expected/CLI.hs
+  testdata/merge3-expected/Text/Format.hs
+  testdata/merge3-expected/Debian/Repo/Slice.hs
+  testdata/merge3-expected/Debian/Repo/Types.hs
+  testdata/merge3-expected/Debian/Repo/Package.hs
+  testdata/merge3-expected/Debian/Repo/Orphans.hs
+  testdata/merge3-expected/Debian/Repo/Types/Slice.hs
+  testdata/merge3-expected/Debian/Repo/Types/AptBuildCache.hs
+  testdata/merge3-expected/Debian/Repo/Types/AptCache.hs
+  testdata/merge3-expected/Debian/Repo/Types/AptImage.hs
+  testdata/merge3-expected/Debian/Repo/Types/PackageIndex.hs
+  testdata/merge3-expected/Debian/Repo/Types/Repository.hs
+  testdata/merge3-expected/Debian/Repo/Types/Release.hs
+  testdata/merge3-expected/Debian/Repo/Sync.hs
+  testdata/merge3-expected/Debian/Repo/AptCache.hs
+  testdata/merge3-expected/Debian/Repo/Monads/Top.hs
+  testdata/merge3-expected/Debian/Repo/Monads/Apt.hs
+  testdata/merge3-expected/Debian/Repo/AptImage.hs
+  testdata/merge3-expected/Debian/Repo/PackageIndex.hs
+  testdata/merge3-expected/Debian/Repo/SourcesList.hs
+  testdata/merge3-expected/Tmp/File.hs
+  testdata/fold5.hs
+  testdata/split4c-expected/Split4.hs
+  testdata/split4c-expected/Split4/A.hs
+  testdata/merge6-expected/B/D.hs
+  testdata/merge6-expected/Test.hs
+  testdata/merge6-expected/A.hs
+  testdata/imports3/NotMain.hs
+  testdata/debian/Debian/VersionPolicy.hs
+  testdata/debian/Debian/Repo/Prelude.hs
+  testdata/debian/Debian/Repo/Slice.hs
+  testdata/debian/Debian/Repo/Package.hs
+  testdata/debian/Debian/Repo/Repo.hs
+  testdata/debian/Debian/Repo/Changes.hs
+  testdata/debian/Debian/Repo/MonadOS.hs
+  testdata/debian/Debian/Repo/OSImage.hs
+  testdata/debian/Debian/Repo/Prelude/Bool.hs
+  testdata/debian/Debian/Repo/Prelude/List.hs
+  testdata/debian/Debian/Repo/Prelude/Process.hs
+  testdata/debian/Debian/Repo/Prelude/Time.hs
+  testdata/debian/Debian/Repo/Prelude/Verbosity.hs
+  testdata/debian/Debian/Repo/Prelude/Misc.hs
+  testdata/debian/Debian/Repo/Prelude/GPGSign.hs
+  testdata/debian/Debian/Repo/Prelude/Files.hs
+  testdata/debian/Debian/Repo/Prelude/SSH.hs
+  testdata/debian/Debian/Repo/Prelude/Lock.hs
+  testdata/debian/Debian/Repo/Prelude/Chroot.hs
+  testdata/debian/Debian/Repo/EnvPath.hs
+  testdata/debian/Debian/Repo/Top.hs
+  testdata/debian/Debian/Repo/SourceTree.hs
+  testdata/debian/Debian/Repo/Dependencies.hs
+  testdata/debian/Debian/Repo/RemoteRepository.hs
+  testdata/debian/Debian/Repo/AptImage.hs
+  testdata/debian/Debian/Repo/Fingerprint.hs
+  testdata/debian/Debian/Repo/State/Slice.hs
+  testdata/debian/Debian/Repo/State/Package.hs
+  testdata/debian/Debian/Repo/State/OSImage.hs
+  testdata/debian/Debian/Repo/State/AptImage.hs
+  testdata/debian/Debian/Repo/State/PackageIndex.hs
+  testdata/debian/Debian/Repo/State/Repository.hs
+  testdata/debian/Debian/Repo/State/Release.hs
+  testdata/debian/Debian/Repo/PackageIndex.hs
+  testdata/debian/Debian/Repo/PackageID.hs
+  testdata/debian/Debian/Repo/Internal/Repos.hs
+  testdata/debian/Debian/Repo/Internal/IO.hs
+  testdata/debian/Debian/Repo/Internal/Apt.hs
+  testdata/debian/Debian/Repo/LocalRepository.hs
+  testdata/debian/Debian/Repo/Release.hs
+  testdata/debian/Debian/Repo.hs
+  testdata/debian/Debian/NewDist/Version.hs
+  testdata/debian/Debian/NewDist/Config.hs
+  testdata/debian/Debian/NewDist/Options.hs
+  testdata/debian/Debian/NewDist/Main.hs
+  testdata/split-expected/Data/Logic/Types/FirstOrderPublic.hs
+  testdata/split-expected/Data/Logic/Types/Propositional.hs
+  testdata/split-expected/Data/Logic/Types/Harrison/FOL.hs
+  testdata/split-expected/Data/Logic/Types/Harrison/Equal.hs
+  testdata/split-expected/Data/Logic/Types/Harrison/Prop.hs
+  testdata/split-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
+  testdata/split-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
+  testdata/split-expected/Data/Logic/Types/FirstOrder.hs
+  testdata/split-expected/Data/Logic/Types/Common.hs
+  testdata/split-expected/Data/Logic/Failing.hs
+  testdata/split-expected/Data/Logic/Resolution.hs
+  testdata/split-expected/Data/Logic/Harrison/FOL.hs
+  testdata/split-expected/Data/Logic/Harrison/Equal.hs
+  testdata/split-expected/Data/Logic/Harrison/Herbrand.hs
+  testdata/split-expected/Data/Logic/Harrison/Meson.hs
+  testdata/split-expected/Data/Logic/Harrison/Normal.hs
+  testdata/split-expected/Data/Logic/Harrison/Tableaux.hs
+  testdata/split-expected/Data/Logic/Harrison/DP.hs
+  testdata/split-expected/Data/Logic/Harrison/Skolem.hs
+  testdata/split-expected/Data/Logic/Harrison/Resolution.hs
+  testdata/split-expected/Data/Logic/Harrison/Prolog.hs
+  testdata/split-expected/Data/Logic/Harrison/PropExamples.hs
+  testdata/split-expected/Data/Logic/Harrison/Lib.hs
+  testdata/split-expected/Data/Logic/Harrison/Prop.hs
+  testdata/split-expected/Data/Logic/Harrison/DefCNF.hs
+  testdata/split-expected/Data/Logic/Harrison/Formulas/Propositional.hs
+  testdata/split-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
+  testdata/split-expected/Data/Logic/Harrison/LICENSE.txt
+  testdata/split-expected/Data/Logic/Harrison/Unif.hs
+  testdata/split-expected/Data/Logic/Satisfiable.hs
+  testdata/split-expected/Data/Logic/Classes/Atom.hs
+  testdata/split-expected/Data/Logic/Classes/Term.hs
+  testdata/split-expected/Data/Logic/Classes/Variable.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/ToPropositional.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/PrettyLit.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/FromLiteral.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/Literal.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/FromFirstOrder.hs
+  testdata/split-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
+  testdata/split-expected/Data/Logic/Classes/Pretty.hs
+  testdata/split-expected/Data/Logic/Classes/Combine.hs
+  testdata/split-expected/Data/Logic/Classes/ClauseNormalForm.hs
+  testdata/split-expected/Data/Logic/Classes/Skolem.hs
+  testdata/split-expected/Data/Logic/Classes/Propositional.hs
+  testdata/split-expected/Data/Logic/Classes/Formula.hs
+  testdata/split-expected/Data/Logic/Classes/Negate.hs
+  testdata/split-expected/Data/Logic/Classes/Apply.hs
+  testdata/split-expected/Data/Logic/Classes/FirstOrder.hs
+  testdata/split-expected/Data/Logic/Classes/Arity.hs
+  testdata/split-expected/Data/Logic/Classes/Equals.hs
+  testdata/split-expected/Data/Logic/Classes/Constants.hs
+  testdata/split-expected/Data/Logic/KnowledgeBase.hs
+  testdata/split-expected/Data/Logic/Instances/PropLogic.hs
+  testdata/split-expected/Data/Logic/Instances/Chiou.hs
+  testdata/split-expected/Data/Logic/Instances/SatSolver.hs
+  testdata/split-expected/Data/Logic/Normal/Clause.hs
+  testdata/split-expected/Data/Logic/Normal/Implicative.hs
+  testdata/split-expected/Data/Boolean.hs
+  testdata/split-expected/Data/Boolean/SatSolver.hs
+  testdata/split2-clean-expected/Split/OtherSymbols.hs
+  testdata/split2-clean-expected/Split/Clean.hs
+  testdata/split2-clean-expected/Split/Instances.hs
+  testdata/split2-clean-expected/Split/ReExported.hs
+  testdata/split2-clean-expected/Split/Foo.hs
+  testdata/split2-clean-expected/Split/Internal/Bar.hs
+  testdata/split2-clean-expected/Split/Internal/Unexp.hs
+  testdata/split2-clean-expected/URL.hs
+  testdata/split5/D.hs
+  testdata/split5/C.hs
+  testdata/split5/E.hs
+  testdata/split5/B.hs
+  testdata/split5/A.hs
+  testdata/split-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
+  testdata/split-merge-expected/Data/Logic/Types/Propositional.hs
+  testdata/split-merge-expected/Data/Logic/Types/Harrison/FOL.hs
+  testdata/split-merge-expected/Data/Logic/Types/Harrison/Equal.hs
+  testdata/split-merge-expected/Data/Logic/Types/Harrison/Prop.hs
+  testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
+  testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
+  testdata/split-merge-expected/Data/Logic/Types/FirstOrder.hs
+  testdata/split-merge-expected/Data/Logic/Types/Common.hs
+  testdata/split-merge-expected/Data/Logic/Failing.hs
+  testdata/split-merge-expected/Data/Logic/Resolution.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/FOL.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Equal.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Herbrand.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Meson.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Normal.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Tableaux.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/DP.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Skolem.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Resolution.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Prolog.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/PropExamples.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Lib.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Prop.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/DefCNF.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
+  testdata/split-merge-expected/Data/Logic/Harrison/LICENSE.txt
+  testdata/split-merge-expected/Data/Logic/Harrison/Unif.hs
+  testdata/split-merge-expected/Data/Logic/Satisfiable.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Atom.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Term.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Variable.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/ToPropositional.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/PrettyLit.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/Literal.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Pretty.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Combine.hs
+  testdata/split-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Skolem.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Propositional.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Formula.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Negate.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Apply.hs
+  testdata/split-merge-expected/Data/Logic/Classes/FirstOrder.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Arity.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Equals.hs
+  testdata/split-merge-expected/Data/Logic/Classes/Constants.hs
+  testdata/split-merge-expected/Data/Logic/KnowledgeBase.hs
+  testdata/split-merge-expected/Data/Logic/Instances/PropLogic.hs
+  testdata/split-merge-expected/Data/Logic/Instances/Chiou.hs
+  testdata/split-merge-expected/Data/Logic/Instances/SatSolver.hs
+  testdata/split-merge-expected/Data/Logic/Normal/Clause.hs
+  testdata/split-merge-expected/Data/Logic/Normal/Implicative.hs
+  testdata/split-merge-expected/Data/Boolean.hs
+  testdata/split-merge-expected/Data/Boolean/SatSolver.hs
+  testdata/Fold7.hs
+  testdata/Text/Format.hs
+  testdata/merge2-expected/Text/Format.hs
+  testdata/merge2-expected/Debian/Repo/Slice.hs
+  testdata/merge2-expected/Debian/Repo/Types.hs
+  testdata/merge2-expected/Debian/Repo/Package.hs
+  testdata/merge2-expected/Debian/Repo/Orphans.hs
+  testdata/merge2-expected/Debian/Repo/Types/AptBuildCache.hs
+  testdata/merge2-expected/Debian/Repo/Types/AptCache.hs
+  testdata/merge2-expected/Debian/Repo/Types/AptImage.hs
+  testdata/merge2-expected/Debian/Repo/Types/PackageIndex.hs
+  testdata/merge2-expected/Debian/Repo/Types/Repository.hs
+  testdata/merge2-expected/Debian/Repo/Types/Common.hs
+  testdata/merge2-expected/Debian/Repo/Types/Release.hs
+  testdata/merge2-expected/Debian/Repo/Sync.hs
+  testdata/merge2-expected/Debian/Repo/AptCache.hs
+  testdata/merge2-expected/Debian/Repo/Monads/Top.hs
+  testdata/merge2-expected/Debian/Repo/Monads/Apt.hs
+  testdata/merge2-expected/Debian/Repo/AptImage.hs
+  testdata/merge2-expected/Debian/Repo/PackageIndex.hs
+  testdata/merge2-expected/Debian/Repo/SourcesList.hs
+  testdata/merge2-expected/Tmp/File.hs
+  testdata/split4b-expected/Split4/B.hs
+  testdata/split4b-expected/Split4/A.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Propositional.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/FOL.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Equal.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Prop.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrder.hs
+  testdata/split-merge-merge-expected/Data/Logic/Types/Common.hs
+  testdata/split-merge-merge-expected/Data/Logic/Failing.hs
+  testdata/split-merge-merge-expected/Data/Logic/Resolution.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/FOL.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Equal.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Herbrand.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Meson.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Normal.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Tableaux.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/DP.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Skolem.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Resolution.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Prolog.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/PropExamples.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Lib.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Prop.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/DefCNF.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/LICENSE.txt
+  testdata/split-merge-merge-expected/Data/Logic/Harrison/Unif.hs
+  testdata/split-merge-merge-expected/Data/Logic/Satisfiable.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Atom.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Term.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Variable.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Pretty.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Combine.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Skolem.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Propositional.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Formula.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Negate.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Apply.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Literal.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/FirstOrder.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Arity.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Equals.hs
+  testdata/split-merge-merge-expected/Data/Logic/Classes/Constants.hs
+  testdata/split-merge-merge-expected/Data/Logic/KnowledgeBase.hs
+  testdata/split-merge-merge-expected/Data/Logic/Instances/PropLogic.hs
+  testdata/split-merge-merge-expected/Data/Logic/Instances/Chiou.hs
+  testdata/split-merge-merge-expected/Data/Logic/Instances/SatSolver.hs
+  testdata/split-merge-merge-expected/Data/Logic/Normal/Clause.hs
+  testdata/split-merge-merge-expected/Data/Logic/Normal/Implicative.hs
+  testdata/split-merge-merge-expected/Data/Boolean.hs
+  testdata/split-merge-merge-expected/Data/Boolean/SatSolver.hs
+  testdata/fold9.hs
+  testdata/logic/Data/Logic/Types/FirstOrderPublic.hs
+  testdata/logic/Data/Logic/Types/Propositional.hs
+  testdata/logic/Data/Logic/Types/Harrison/FOL.hs
+  testdata/logic/Data/Logic/Types/Harrison/Equal.hs
+  testdata/logic/Data/Logic/Types/Harrison/Prop.hs
+  testdata/logic/Data/Logic/Types/Harrison/Formulas/Propositional.hs
+  testdata/logic/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
+  testdata/logic/Data/Logic/Types/FirstOrder.hs
+  testdata/logic/Data/Logic/Types/Common.hs
+  testdata/logic/Data/Logic/Failing.hs
+  testdata/logic/Data/Logic/Resolution.hs
+  testdata/logic/Data/Logic/Harrison/FOL.hs
+  testdata/logic/Data/Logic/Harrison/Equal.hs
+  testdata/logic/Data/Logic/Harrison/Herbrand.hs
+  testdata/logic/Data/Logic/Harrison/Meson.hs
+  testdata/logic/Data/Logic/Harrison/Normal.hs
+  testdata/logic/Data/Logic/Harrison/Tableaux.hs
+  testdata/logic/Data/Logic/Harrison/DP.hs
+  testdata/logic/Data/Logic/Harrison/Skolem.hs
+  testdata/logic/Data/Logic/Harrison/Resolution.hs
+  testdata/logic/Data/Logic/Harrison/Prolog.hs
+  testdata/logic/Data/Logic/Harrison/PropExamples.hs
+  testdata/logic/Data/Logic/Harrison/Lib.hs
+  testdata/logic/Data/Logic/Harrison/Prop.hs
+  testdata/logic/Data/Logic/Harrison/DefCNF.hs
+  testdata/logic/Data/Logic/Harrison/Formulas/Propositional.hs
+  testdata/logic/Data/Logic/Harrison/Formulas/FirstOrder.hs
+  testdata/logic/Data/Logic/Harrison/LICENSE.txt
+  testdata/logic/Data/Logic/Harrison/Unif.hs
+  testdata/logic/Data/Logic/Satisfiable.hs
+  testdata/logic/Data/Logic/Classes/Atom.hs
+  testdata/logic/Data/Logic/Classes/Term.hs
+  testdata/logic/Data/Logic/Classes/Variable.hs
+  testdata/logic/Data/Logic/Classes/Pretty.hs
+  testdata/logic/Data/Logic/Classes/Combine.hs
+  testdata/logic/Data/Logic/Classes/ClauseNormalForm.hs
+  testdata/logic/Data/Logic/Classes/Skolem.hs
+  testdata/logic/Data/Logic/Classes/Propositional.hs
+  testdata/logic/Data/Logic/Classes/Formula.hs
+  testdata/logic/Data/Logic/Classes/Negate.hs
+  testdata/logic/Data/Logic/Classes/Apply.hs
+  testdata/logic/Data/Logic/Classes/Literal.hs
+  testdata/logic/Data/Logic/Classes/FirstOrder.hs
+  testdata/logic/Data/Logic/Classes/Arity.hs
+  testdata/logic/Data/Logic/Classes/Equals.hs
+  testdata/logic/Data/Logic/Classes/Constants.hs
+  testdata/logic/Data/Logic/KnowledgeBase.hs
+  testdata/logic/Data/Logic/Instances/PropLogic.hs
+  testdata/logic/Data/Logic/Instances/Chiou.hs
+  testdata/logic/Data/Logic/Instances/SatSolver.hs
+  testdata/logic/Data/Logic/Normal/Clause.hs
+  testdata/logic/Data/Logic/Normal/Implicative.hs
+  testdata/logic/Data/Boolean.hs
+  testdata/logic/Data/Boolean/SatSolver.hs
+  testdata/imports6-expected/EndComment.hs
+  testdata/split4-expected/Split4/BinaryPackagesOfIndex.hs
+  testdata/split4-expected/Split4/SourcePackagesOfIndex.hs
+  testdata/split4-expected/Split4/GetPackages.hs
+  testdata/split1-expected/Text/Format.hs
+  testdata/split1-expected/Debian/Repo/Slice.hs
+  testdata/split1-expected/Debian/Repo/Types.hs
+  testdata/split1-expected/Debian/Repo/Orphans.hs
+  testdata/split1-expected/Debian/Repo/Types/Slice.hs
+  testdata/split1-expected/Debian/Repo/Types/Repo.hs
+  testdata/split1-expected/Debian/Repo/Types/EnvPath.hs
+  testdata/split1-expected/Debian/Repo/Types/AptBuildCache.hs
+  testdata/split1-expected/Debian/Repo/Types/AptCache.hs
+  testdata/split1-expected/Debian/Repo/Types/AptImage.hs
+  testdata/split1-expected/Debian/Repo/Types/PackageIndex.hs
+  testdata/split1-expected/Debian/Repo/Types/Repository.hs
+  testdata/split1-expected/Debian/Repo/Types/Release.hs
+  testdata/split1-expected/Debian/Repo/Sync.hs
+  testdata/split1-expected/Debian/Repo/AptCache.hs
+  testdata/split1-expected/Debian/Repo/Package/BinarySourceVersion.hs
+  testdata/split1-expected/Debian/Repo/Package/SourceFilePaths.hs
+  testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryIDs.hs
+  testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryNames.hs
+  testdata/split1-expected/Debian/Repo/Package/SourceBinaryNames.hs
+  testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfCachedIndex.hs
+  testdata/split1-expected/Debian/Repo/Package/ReleaseSourcePackages.hs
+  testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfCachedIndex.hs
+  testdata/split1-expected/Debian/Repo/Package/ToSourcePackage.hs
+  testdata/split1-expected/Debian/Repo/Package/ToBinaryPackage.hs
+  testdata/split1-expected/Debian/Repo/Package/ReleaseBinaryPackages.hs
+  testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfIndex.hs
+  testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfIndex.hs
+  testdata/split1-expected/Debian/Repo/Package/ReExported.hs
+  testdata/split1-expected/Debian/Repo/Package/GetPackages.hs
+  testdata/split1-expected/Debian/Repo/Package/PutPackages.hs
+  testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceVersion.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/BinarySourceVersion.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/UseConstants.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/OtherSymbols.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/TryParseRel.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/IndexPrefix.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/ParseSourceParagraph.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/UriToString.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/Constants.hs
+  testdata/split1-expected/Debian/Repo/Package/Internal/IndexCacheFile.hs
+  testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceID.hs
+  testdata/split1-expected/Debian/Repo/Monads/Top.hs
+  testdata/split1-expected/Debian/Repo/Monads/Apt.hs
+  testdata/split1-expected/Debian/Repo/AptImage.hs
+  testdata/split1-expected/Debian/Repo/PackageIndex.hs
+  testdata/split1-expected/Debian/Repo/SourcesList.hs
+  testdata/split1-expected/Tmp/File.hs
+  testdata/fold3b/Main.hs
+  testdata/split4/Split4.hs
+  testdata/split2/URL.hs
+  testdata/split2/Split.hs
+  testdata/Tmp/File.hs
+  testdata/imports7/CLI.hs
+  testdata/merge4-expected/Out.hs
+  testdata/merge4-expected/M1.hs
+  testdata/imports6/EndComment.hs
+  testdata/split2-expected/Split/OtherSymbols.hs
+  testdata/split2-expected/Split/Clean.hs
+  testdata/split2-expected/Split/Instances.hs
+  testdata/split2-expected/Split/ReExported.hs
+  testdata/split2-expected/Split/Foo.hs
+  testdata/split2-expected/Split/Internal/Bar.hs
+  testdata/split2-expected/Split/Internal/Unexp.hs
+  testdata/split2-expected/URL.hs
+  testdata/split5-expected/D.hs
+  testdata/split5-expected/C.hs
+  testdata/split5-expected/E.hs
+  testdata/split5-expected/B/B.hs
+  testdata/split5-expected/B/A.hs
+  testdata/split5-expected/A.hs
 
 Flag build-tests
   Description: build the test executable
@@ -34,13 +511,6 @@
     Language.Haskell.Modules.Util.Symbols,
     Language.Haskell.Modules.Util.Temp,
     Language.Haskell.Modules.Util.Test
-  Other-Modules:
-    Tests.Fold,
-    Tests.Imports,
-    Tests.Merge,
-    Tests.Split
-    Tests.SrcLoc
-    Tests.Symbols
 
   Build-Depends:
     applicative-extras,
@@ -69,25 +539,17 @@
   Hs-source-dirs: scripts
   Build-Depends:
     base,
+    Cabal,
     containers,
-    data-default,
+    cmdargs,
     directory,
-    filepath,
-    haskell-src-exts,
-    HUnit,
-    lifted-base,
+    haskeline,
+    lens,
     module-management,
     monad-control,
     mtl,
-    process,
     set-extra,
-    syb,
-    temporary,
-    haskeline == 0.7.*,
-    transformers-base,
-    Cabal,
-    cmdargs,
-    lens
+    transformers-base
 
   -- In ghc-7.6.3, -O0 prevents one of two failures: either a GHC out
   -- of memory error, or an out of simplifier ticks error:
@@ -102,34 +564,15 @@
   -- In 7.8.1 increasing the context stack size from 20 to 60 seems necessary.
   ghc-options: -O0 -fcontext-stack=60
 
-Executable tests
-  Main-is: Tests.hs
-  if flag (build-tests)
-    Buildable: True
-    Build-Depends:
-      ansi-wl-pprint,
-      base,
-      bytestring,
-      Cabal,
-      containers,
-      data-default,
-      debian,
-      directory,
-      Extra,
-      filepath,
-      HUnit,
-      haskell-src-exts,
-      monad-control,
-      module-management,
-      lifted-base,
-      mtl,
-      process,
-      process-progress,
-      pureMD5,
-      regex-compat,
-      set-extra,
-      syb,
-      system-fileio,
-      temporary
-  else
-    Buildable: False
+Test-Suite module-management-tests
+  Type: exitcode-stdio-1.0
+  Hs-Source-Dirs: Tests
+  Main-is: Main.hs
+  Build-Depends:
+    base,
+    containers,
+    filepath,
+    HUnit,
+    haskell-src-exts,
+    module-management,
+    process
diff --git a/scripts/CLI.hs b/scripts/CLI.hs
--- a/scripts/CLI.hs
+++ b/scripts/CLI.hs
@@ -1,5 +1,5 @@
 {- # OPTIONS_GHC -Wall #-}
-{-# LANGUAGE ViewPatterns, PatternGuards, DeriveDataTypeable #-}
+{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, PatternGuards, StandaloneDeriving, ViewPatterns #-}
 module Main where
 
 import System.Console.Haskeline
@@ -15,6 +15,9 @@
 import Data.Char (toLower)
 import qualified Data.Set as S (member, toList, map)
 import Data.Set.Extra as Set (Set, toList)
+import Distribution.ModuleExport (ModuleExport(..))
+import Distribution.Package (InstalledPackageId(..))
+import GHC.Generics (Generic)
 import Language.Haskell.Modules (cleanImports, CleanT, findHsModules, mergeModules, modifyDirs, ModuleName(..), MonadClean, noisily, putDirs, putModule, runCleanT, splitModuleDecls)
 import Language.Haskell.Modules.ModuVerse (getNames)
 import Language.Haskell.Modules.SourceDirs (getDirs)
@@ -39,6 +42,9 @@
 import Control.Exception (fromException)
 
 import System.Exit
+
+deriving instance Generic (ModuleExport m)
+deriving instance Generic InstalledPackageId
 
 data HMM = CLI
   { verbosity :: Int,
diff --git a/testdata.tar.gz b/testdata.tar.gz
deleted file mode 100644
Binary files a/testdata.tar.gz and /dev/null differ
diff --git a/testdata/Equal.hs b/testdata/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/Equal.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Types.Harrison.Equal where
+
+instance C.Formula (Formula FOLEQ) FOLEQ => P.PropositionalFormula (Formula FOLEQ) FOLEQ where
+    foldPropositional co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
diff --git a/testdata/Fold7.hs b/testdata/Fold7.hs
new file mode 100644
--- /dev/null
+++ b/testdata/Fold7.hs
@@ -0,0 +1,7 @@
+module Main where
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex repo release index =
+    liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+a=1  -- This is a comment too
diff --git a/testdata/Text/Format.hs b/testdata/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/Tmp/File.hs b/testdata/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
diff --git a/testdata/debian/Debian/NewDist/Config.hs b/testdata/debian/Debian/NewDist/Config.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/NewDist/Config.hs
@@ -0,0 +1,528 @@
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+-- |The 'Config' module implements both command line and configuration
+-- file option handling.  It converts the command line parameters into
+-- Flag objects, and then expands any Include, Use, or Config flags.
+-- Every command line parameter has an equivalent format that can
+-- appear in the configuration file.  Using Debian conventions, a
+-- command line option such as
+--
+--    --some-option=value
+--
+-- could appear in a configuration using this syntax:
+--
+--    Some-Option: value
+--
+-- This is the format used in Debian Control files, and is similar to
+-- the format described in RFC-922.  Note that a value in a control
+-- file can be continued onto multiple lines by prefixing the extra
+-- lines with whitespace, as described here:
+-- <http://www.debian.org/doc/debian-policy/ch-controlfields.html>
+--
+-- See the documentation of the Flag datatype below for a description
+-- of the features this module supports.
+--
+-- Author: David Fox <ddssff@gmail.com>
+module Config
+    ( Flag(..)
+    -- , seedFlags
+    , optBaseSpecs
+    , computeConfig
+    , configPath
+    , findValues
+    , findValue
+    , findBool
+    -- , values
+    , ParamDescr(..)
+    , option
+    , Config.usageInfo
+    ) where
+
+import Control.Exception
+import Control.Monad
+import Data.List
+import Data.Maybe
+import Debian.Control
+import System.Console.GetOpt
+import System.Directory
+import System.Environment as Environment
+import System.IO
+import Text.Regex
+
+-- | The command line arguments are converted to a list of 'Flag'
+-- objects, and this list is then expanded by the 'computeConfig'
+-- function and the result (due to the operation of 'Use') is a list
+-- of Flag lists.
+data Flag
+    = Include FilePath |
+      -- ^ (Command line equivalents: @--include@ or @--config@.)  The FilePath
+      -- value is a file or directory containing configuration information.
+      -- If no 'Include' option is given, default locations will be used as
+      -- described in the 'computeConfig' function.
+      Name String |
+      -- ^ (No command line equivalent).  This flag is used to
+      -- assign a name to a paragraph, which can then be referred to by a
+      -- 'Use' flag.
+      Use [String] |
+      -- ^ (Command line equivalent: @--use@.)  The 'Use' flag is
+      -- refers to one or more 'Name'ed paragraphs.  Referring to a
+      -- single named paragraph causes the parameters in that
+      -- paragraph to be added to the set of parameters we are
+      -- computing.  If several paragraphs are named, a copy the
+      -- current set of parameters is created for each named
+      -- paragraph, and the result is several sets of parameters.
+      -- This is what causes the result of the 'computeConfig' function
+      -- to be a list of 'Flag' lists.
+      Let String String |
+      -- ^ (Command line equivalent: @--let name=value@.)  Define a
+      -- macro @name@, occurrences of @${name}@ will expand to @value@.
+      Value String String
+      -- ^ (No command line equivalent.)  Any other named parameter becomes
+      -- a 'Value' flag.
+      deriving Eq
+
+isUse :: Flag -> Bool
+isUse (Use _) = True
+isUse _ = False
+
+-- |Display a flag in (pseudo) RFC-922 format: @Name: Value@
+instance Show Flag where
+    show (Include x) = "Include: " ++ x
+    show (Name x) = "Name: " ++ x
+    show (Use xs) = "Use: " ++ consperse " " xs
+    show (Let name value) = "Let:" ++ name ++ "=" ++ value
+    show (Value name value) = name ++ ": " ++ value
+
+instance Read Flag where
+    readsPrec _ =
+        let re = mkRegex "^([^ \t:]+):([^\n]*(\n[ \t].*)*)($|\n)" in
+        (\ s ->
+             case matchRegexAll re s of
+               Just (_, _, after, [name, value, _, _]) ->
+                   case name of
+                     "Include" -> [(Include (stripWS value), after)]
+                     "Config"  -> [(Include (stripWS value), after)]
+                     "Name" -> [(Name (stripWS value), after)]
+                     "Use" -> [(Use (words value), after)]
+                     "Let" -> [(parseLet value, after)]
+                     _ -> [(Value (stripWS name) (stripWS value), after)]
+               _ -> error ("Parse error reading flag: '" ++ s ++ "'"))
+
+{-
+-- | Convert a list of command line arguments into a set of flags.
+-- These seed flags are later expanded by applying the Name\/Use
+-- mechanism to the information loaded from the configuration file or
+-- directory.  The appName argument is used to construct the usage
+-- string when invalid arguments are given.
+seedFlags :: String -> [ParamDescr a] -> [String] -> [Flag]
+seedFlags appName options args =
+    -- Convert the command line arguments to flags.  Any arguments
+    -- not recognized by getOpt is treated as implicit "--use"
+    -- parameter.
+    case getOpt Permute (map option customizedOptions) args of
+      (o, [], []) -> o
+      (o, extra, []) -> (o ++ [Use extra])
+      (_, _, errs) -> error (concat errs ++ Debian.Config.usageInfo header customizedOptions)
+    where
+      customizedOptions = mergeSpecs appName options
+      header = "Usage: " ++ appName ++ " [OPTION...]"
+-}
+
+-- |Return the configuration information computed from a set of seed
+-- flags and the configuration files.
+-- Example:
+--
+-- > > computeConfig "autobuilder" Nothing [Include "/home/build/.autobuilder.d"]
+-- > [[Name "common",Value "Vendor-Tag" "cnr",Value "Release-Tag" "1", ...
+computeConfig :: Int			-- ^ Preliminary verbosity level, before we have obtained the verbosity parameter
+              -> String			-- ^ The application name, used to construct candidate configuration file names.
+              -> [Flag]			-- ^ The set of flags read from the configuration
+              -> ([[Flag]] -> [[Flag]])	-- ^ Final preparation of the configuration file contents
+              -> IO [[Flag]]		-- ^ The result is a list of flag lists.  See 'Use' for an explanation of how you would get
+					-- more than one flag list here.
+computeConfig verbosity appName commandLineFlags prepare =
+    do when (verbosity > 2) (hPutStrLn stderr $ "computeConfig: commandLineFlags=" ++ show commandLineFlags)
+       -- Compute the configuration file path and then load and expand it.
+       defaultIncludes <- defaultConfigPaths appName
+       --hPutStrLn stderr $ "defaultIncludes " ++ show defaultIncludes
+       configFlags <- configPath verbosity (commandLineFlags ++ defaultIncludes) >>=
+                      tryPaths . maybeToList >>= expandIncludes >>= return . prepare
+       when (verbosity > 2) (hPutStrLn stderr $ "computeConfig: configFlags=" ++ show commandLineFlags)
+       -- Expand the command line flags using the Use/Name mechanism, and then expand the lets.
+       expandSections [commandLineFlags] configFlags >>= return . map (expandLets . checkLets)
+    where
+      -- Load a list of configuration files.
+      expandIncludes :: [[Flag]] -> IO [[Flag]]
+      expandIncludes flags =
+          do when (verbosity > 2) (hPutStrLn stderr $ "expandIncludes: flags=" ++ show flags)
+             -- ePut ("flags: " ++ show flags)
+             let paths = concat (map includes flags)
+             when (verbosity > 2) (hPutStrLn stderr $ "expandIncludes: paths=" ++ show paths)
+             -- ePut (concat (map (("Include: " ++) . (++ "\n")) paths))
+             case paths of
+               [] -> return flags
+               _ ->
+                   do newflags <- tryPaths paths
+                      when (verbosity > 2) (hPutStrLn stderr ("expandIncludes: newflags=" ++ show newflags))
+                      return (flags ++ newflags)
+      checkLets :: [Flag] -> [Flag]
+      checkLets flags =
+          concat (map (checkLetGroup . nub) (groupBy sameLetName (sortBy compareLetName flags)))
+          where
+            compareLetName (Let a _) (Let b _) = compare a b
+            compareLetName (Let _ _) _ = LT
+            compareLetName _ (Let _ _) = GT
+            compareLetName _ _ = EQ
+            sameLetName a b = compareLetName a b == EQ
+            checkLetGroup :: [Flag] -> [Flag]
+            checkLetGroup x@[Let _ _] = x
+            checkLetGroup xs@(Let a _ : _) = error $ "Multiple definitions of " ++ a ++ ": " ++ show xs
+            checkLetGroup xs = xs
+      -- Find the Let flags and use them to perform expansions on
+      -- all the flags in the list.
+      expandLets :: [Flag] -> [Flag]
+      expandLets flags = map expand flags
+          where
+            expand (Include path) = Include (expand' path)
+            expand (Name s) = Name (expand' s)
+            expand (Use l) = Use (map expand' l)
+            -- Not expanding let names or parameter names.
+            expand (Let name value) = Let (expand' name) (expand' value)
+            expand (Value name value) = Value (expand' name) (expand' value)
+            -- Do expansions of a string.  S is a variable name followed by "}"
+            -- and maybe some more text.
+            expand' ('$' : '{' : s) = expand'' s alist
+            expand' (x : s) = x : expand' s
+            expand' "" = ""
+            -- Try each expansion in turn on the name at the beginning of S.
+            expand'' s [] = "${" ++ expand' s ++ "}"	-- no expansion possible
+            expand'' s ((name, value) : etc) =
+                if isPrefixOf (name ++ "}") s then
+                    value ++ expand' (drop (length name + 1) s) else
+                    expand'' s etc
+            alist = lets flags
+      -- Select the --include arguments from a list of flags
+      includes (Include path : etc) = path : includes etc
+      includes (_ : etc) = includes etc
+      includes [] = []
+      -- Select the --let arguments from a flag list and return an alist
+      lets :: [Flag] -> [(String, String)]
+      lets (Let name value : etc) = (name, value) : lets etc
+      lets (_ : etc) = lets etc
+      lets [] = []
+
+-- |Find the configuration file or directory we will use, if any.
+-- This the first file or directory that exists among these:
+--
+-- * The 'Include' elements from the list of flags 
+--
+-- * @$HOME\/.\<appName\>.d@
+--
+-- * @$HOME\/.\<appName\>.conf@
+--
+-- * @\/etc\/\<appName\>.d@
+--
+-- * @\/etc\/\<appName\>.conf@
+configPath :: Int -> [Flag] -> IO (Maybe FilePath)
+configPath verbosity flags =
+    do when (verbosity > 1) (hPutStrLn stderr ("Seed flags: " ++ show flags))
+       path <- filterM exists (findIncludes flags) >>= return . listToMaybe
+       when (verbosity > 0) (hPutStrLn stderr ("Configuration file: " ++ show path))
+       return path
+    where
+      exists path =
+          do fileExists <- doesFileExist path
+             dirExists <- doesDirectoryExist path
+             return (fileExists || dirExists)
+      -- Find the value of the --include and (synonymous) --config flags
+      findIncludes [] = []
+      findIncludes (Include path : etc) = path : findIncludes etc
+      findIncludes (_ : etc) = findIncludes etc
+
+-- | Return a list of paths where we might find a configuration file
+-- or directory.
+defaultConfigPaths :: String -> IO [Flag]
+defaultConfigPaths appName =
+    do paths1 <- try (getEnv "HOME") >>=
+                 return . either (\ (_ :: SomeException) -> []) 
+                                 (\ home -> [home ++ "/." ++ appName ++ ".d",
+                                             home ++ "/." ++ appName ++ ".conf"])
+       let paths2 = ["/etc/" ++ appName ++ ".d",
+                     "/etc/" ++ appName ++ ".conf"]
+       return $ map Include (paths1 ++ paths2)
+
+-- Load a list of configuration files.
+tryPaths :: [FilePath] -> IO [[Flag]]
+tryPaths paths = do
+    --hPutStrLn stderr ("tryPaths " ++ show paths)
+    flags <- mapM tryPath paths >>= return . mergeControls >>= return . flagsOfControl
+    return flags
+    where
+      -- Each paragraph of the control file becomes a list of flags
+      flagsOfControl (Control paragraphs) = map (\ (Paragraph fields) -> catMaybes (map flagOfField fields)) paragraphs
+      flagOfField (Field (name, value)) = Just . read $ name ++ ": " ++ value
+      flagOfField _ = Nothing
+      tryPath path =
+	  do
+            --hPutStrLn stderr (" tryPath " ++ show path)
+            isDir <- doesDirectoryExist path
+            case isDir of
+              False -> do
+                  try (parseControlFromFile path) >>=
+                      either (\ (e :: SomeException) -> error . ((path ++ ": ") ++) . show $ e) return >>=
+	              either (\ e -> error . ((path ++ ": ") ++) . show $ e) return
+              True -> do
+                   getDirectoryContents path >>=
+                      return . map ((path ++ "/") ++) . sort . filter isConfigPart >>=
+                      mapM tryPath >>=
+                      return . mergeControls
+      isConfigPart "" = False
+      isConfigPart s | {- isDigit (head s) && -} head s /= '.' && last s /= '~' && s /= "_darcs" = True
+      isConfigPart _ = False
+
+-- |Expand occurrences of @--use@ in a list of flag lists.  The
+-- expansion is appended (not prepended) to the list of flags so that
+-- the command line and global options remain at the beginning of the
+-- list.
+expandSections :: [[Flag]] -> [[Flag]] -> IO [[Flag]]
+expandSections toExpand expansions =
+    do
+      expanded <- mapM (expandSection []) toExpand
+      return (concat expanded)
+    where
+      expandSection :: [String] -> [Flag] -> IO [[Flag]]
+      expandSection stack xs =
+          do
+            -- ePut ("stack: " ++ show stack)
+            -- ePut ("toExpand: " ++ show xs)
+            -- ePut ("expansions: " ++ show expansions)
+            let (useFlags, otherFlags) = partition isUse xs
+            -- ePut ("useFlags: " ++ show useFlags)
+            let sequences = map getNames useFlags
+            -- ePut ("sequences: " ++ show sequences)
+            -- A sequence of name lists
+            let (sequences' :: [[String]]) = cartesianProduct sequences
+            -- ePut ("sequence: " ++ show sequence)
+            -- map (elem stack) (concat sequence)
+            let (newstack :: [String]) = nub $ stack ++ concat sequences'
+            -- Remove any names which are already being expanded,
+            -- either because of a cycle in the Use->Name graph or
+            -- because an element is reachable by multiple paths.
+            let (sequence''' :: [[String]]) = map (\ s -> filter (\ e -> not (elem e stack)) s) sequences'
+            -- ePut ("newstack: " ++ show newstack)
+            -- A sequence of flag lists
+            let (sequence' :: [[[Flag]]]) = map (expandNames stack) sequence'''
+            -- ePut ("sequence': " ++ show sequence')
+            case sequence' of
+              [] ->
+                  return [otherFlags]
+              _ ->
+                  do
+                    let sequence'' = map (otherFlags ++) (map concat sequence')
+                    -- ePut ("sequence'': " ++ show sequence'')
+                    result <- mapM (expandSection newstack) sequence''
+                    -- ePut ("result: " ++ show result)
+                    return (concat result)
+
+      getNames (Use xs) = xs
+      getNames _ = []
+
+      -- FIXME: use the stack to prevent infinite recursion
+      expandNames stack names' =
+          map (expandName stack) names'
+      expandName _stack name =
+          maybe err id (find  (elem (Name name)) expansions)
+          where
+            err = error ("Configuration file section '" ++ name ++ "' not found.\nAvailable:\n  " ++ 
+                         (consperse "\n  " . map show . concat . map (filter isName) $ expansions))
+            isName (Name _) = True
+            isName _ = False
+
+--ePut = hPutStrLn stderr
+
+-- |Command line option specifications.  The caller passes in a list of
+-- options, and three additional standard options are added here:
+-- --config <path> - specify the path to a configuration file
+-- --include <path> - pull in options from a file
+-- --use 'name1 name2 ...' - pull in some named sections
+{-
+mergeSpecs :: String -> [ParamDescr a] -> [ParamDescr a]
+mergeSpecs appName specs =
+    specs ++ optBaseSpecs appName
+-}
+
+optBaseSpecs :: String -> [ParamDescr Flag]
+optBaseSpecs appName =
+    [ Param { shortOpts = ['c']
+            , longOpts = ["config","include"]
+            , argDescr = (ReqArg Include "PATH")
+            , names = ["Config", "Include"]
+            , description =
+                (consperse "\n"
+                 ["Location of the configuration file or directory to",
+                  "suppliment the command line arguments.  This option",
+                  "may be given multiple times, but only the first",
+                  "one that exists is used.  Furthermore, if none are supplied or",
+                  "none of those supplied exist, four additional paths are tried,",
+                  "in this order: '/etc/" ++ appName ++ ".d', '/etc/" ++ appName ++ ".conf',",
+                  "'$HOME/." ++ appName ++ ".d', and '$HOME/." ++ appName ++ ".conf'.",
+                  "If the configuration path specifies a directory all the files",
+                  "in the directory that begin with digits are read in lexical",
+                  "order and merged.  If it is a regular file, it is read and",
+                  "the result is used."]) }
+    , Param { shortOpts = []
+            , longOpts = ["use"]
+            , argDescr = (ReqArg (Use . words) "NAME")
+            , names = ["Use"]
+            , description =
+                (consperse "\n"
+                 ["When a Use: NAME parameter appears the configuration parameters",
+                  "are searched for a configuration section containing Name: NAME,",
+                  "and the values in that section are substituted for the occurrence",
+                  "of Use.  This expansion is performed repeatedly until no more Use",
+                  "parameters are left.  If an argument appears without an associated",
+                  "flag, it is treated as an implied Use."]) }
+    , Param { shortOpts = []
+            , longOpts = ["let"]
+            , argDescr = (ReqArg parseLet "SYM=VAL")
+            , names = ["Let"]
+            , description =
+                "define SYM to be VAL, ${SYM} will be expanded to VAL wherever it appears." }
+    ]
+
+-- |Return all values of a string paramter in a flag list.
+findValues :: [Flag] -> String -> [String]
+findValues (Value name value : etc) sought | name == sought = value : (findValues etc sought)
+findValues (_ : etc) sought = findValues etc sought
+findValues [] _ = []
+
+-- |Return the value of a string paramter in a flag list.
+findValue :: [Flag] -> String -> Maybe String
+findValue flags sought =
+    case catMaybes . map matchName $ flags of
+      [] -> Nothing
+      x -> Just . last $ x
+    where
+      matchName (Value name value) = if name == sought then Just value else Nothing
+      matchName _ = Nothing
+
+-- |Return the value of a boolean paramter in a flag list.
+findBool :: [Flag] -> String -> Bool
+findBool flags sought = maybe False (\ _ -> True) (findValue flags sought)
+
+parseLet :: String -> Flag
+parseLet s =
+    case matchRegex re s of
+      Just [a, _, b, _] -> Let a b
+      _ -> error ("Invalid define: " ++ s)
+    where
+      re = mkRegex ("^" ++ mw ++ nwn ++ mw ++ "=" ++ mw ++ nwn ++ mw ++ "$")
+      nwn = "([^ \t](.*[^ \t])?)"	-- nonwhite, white, nonwhite
+      mw = "[ \t]*"			-- maybe white
+
+-- |The mighty consperse function
+consperse :: [a] -> [[a]] -> [a]
+consperse sep items = concat (intersperse sep items)
+
+-- |cartesianProduct [[1,2,3], [4,5],[6]] -> [[1,4,6],[1,5,6],[2,4,6],[2,5,6],[3,4,6],[3,5,6]]
+cartesianProduct :: [[a]] -> [[a]]
+cartesianProduct [] = []
+cartesianProduct [xs] = map (: []) xs
+cartesianProduct (xs : yss) = distribute xs (cartesianProduct yss)
+distribute :: [a] -> [[a]] -> [[a]]
+distribute xs yss = concat (map (\ x -> map (x :) yss) xs)
+
+-- Example:
+--
+-- let (optSpecs :: [OptDescr Flag]) =
+--     [Option [] ["verbose"]	(ReqArg (Value "Verbose") "NUMBER")
+--	"Specify the amount of debugging output",
+--	Option ['n'] ["dry-run"]	(NoArg (Value "Dry-Run" "yes"))
+--	"Test run, don't modify the repository."]
+-- flags <- computeConfig "myapp" optSpecs Nothing >>= return . head
+-- let dryRun = findBool flags "Dry-Run"
+--     verbose = maybe 0 read (findValue flags "Verbose")
+--
+
+-- When this is executed, it will load either the configuration file
+-- ~/.myapp.conf, /etc/myapp.conf, or some configuration file
+-- specified using the --config command line app.  The top section of
+-- the configuration file will be merged with the command line flags.
+-- Then those flags are expanded using the rules described in the
+-- definition of the Flag datatype, and the result is returned.
+
+-- |This is basically an augmented version of the OptDescr type from
+-- System.Console.GetOpt.  It adds a list of parameter names which are
+-- used in the configuration file, and a function to retrieve the
+-- available values for the parameter.
+data ParamDescr a
+    = Param { --option :: OptDescr Flag
+              shortOpts :: [Char]
+	    , longOpts :: [String]
+            , names :: [String]
+	    , argDescr :: (ArgDescr a)
+	    , description :: String
+            }
+
+option :: ParamDescr a -> OptDescr a
+option p =
+    Option (shortOpts p) (longOpts p) (argDescr p) (description p)
+
+-- Modified version of usageInfo.
+
+data DescrLine
+    = Opt { long :: String
+          , short :: String
+          , param :: String }
+    | Text String
+
+-- |Modified version of System.Console.GetOpt.usageInfo, avoids printing
+-- such wide lines.
+usageInfo :: String		-- header
+          -> [ParamDescr a]	-- parameter descriptor
+          -> String		-- nicely formatted decription of options
+usageInfo header params = unlines (header:table)
+   where xs = concatMap fmtOpt params
+         ssl = foldl max 0 (map ss xs)
+         lsl = foldl max 0 (map ls xs)
+         ss (Opt {short = s}) = length s
+         ss _ = 0
+         ls (Opt {long = s}) = length s
+         ls _ = 0
+         table = map fmtLine (legend ++ xs)
+         fmtLine (Text s) = "    " ++ s
+         fmtLine (Opt {long = lopts, short = sopts, param = ps}) =
+             "  " ++
+             flushLeft lsl lopts ++ "  " ++
+             flushLeft ssl sopts ++ "  " ++
+             ps
+         flushLeft n x = take n (x ++ repeat ' ')
+         legend = [Opt {long = "Long option", short = "Short option", param = "Config file parameter"},
+                   Opt {long = "-----------", short = "------------", param = "---------------------"}]
+
+fmtOpt :: ParamDescr a -> [DescrLine]
+fmtOpt paramDescr =
+   let Option sos los ad descr = option paramDescr in
+   let ds = [Text ""] ++ map Text (lines descr) ++ [Text ""]
+       ss = map (fmtShort ad) sos
+       ls = map (fmtLong  ad) los
+       ps = map (fmtParam ad) (names paramDescr) in
+   let n = max (length ps) (max (length ls) (length ss)) in
+   let ss' = ss ++ replicate (n - length ss) ""
+       ls' = ls ++ replicate (n - length ls) ""
+       ps' = ps ++ replicate (n - length ps) "" in
+   map (\ (l, s, p) -> Opt {long = l, short = s, param = p}) (zip3 ls' ss' ps') ++ ds
+
+fmtShort :: ArgDescr a -> Char -> String
+fmtShort (NoArg  _   ) so = "-" ++ [so]
+fmtShort (ReqArg _ ad) so = "-" ++ [so] ++ " " ++ ad
+fmtShort (OptArg _ ad) so = "-" ++ [so] ++ "[" ++ ad ++ "]"
+
+fmtLong :: ArgDescr a -> String -> String
+fmtLong (NoArg  _   ) lo = "--" ++ lo
+fmtLong (ReqArg _ ad) lo = "--" ++ lo ++ "=" ++ ad
+fmtLong (OptArg _ ad) lo = "--" ++ lo ++ "[=" ++ ad ++ "]"
+
+fmtParam :: ArgDescr a -> String -> String
+fmtParam (NoArg  _   ) po = po ++ ": Yes"
+fmtParam (ReqArg _ ad) po = po ++ ": " ++ ad
+fmtParam (OptArg _ ad) po = po ++ ": " ++ ad
diff --git a/testdata/debian/Debian/NewDist/Main.hs b/testdata/debian/Debian/NewDist/Main.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/NewDist/Main.hs
@@ -0,0 +1,214 @@
+{-# LANGUAGE OverloadedStrings, TupleSections #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Replacement for debpool.
+module Main where
+
+import Control.Monad (when)
+import Control.Monad.Trans (liftIO)
+import Data.Maybe (catMaybes)
+import Data.Set (Set, fromList)
+import Data.Text (pack)
+import Debian.Arch (Arch(Binary, Source), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Changes (ChangesFile(..))
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (BinPkgName)
+import Debian.Release (ReleaseName(ReleaseName), releaseName', parseReleaseName, Section, parseSection')
+import Debian.Repo.EnvPath (EnvPath(EnvPath), EnvRoot(EnvRoot), envPath, outsidePath)
+import Debian.Repo.Internal.Repos (MonadRepos, runReposT)
+import Debian.Repo.LocalRepository (LocalRepository, Layout, repoRoot, setRepositoryCompatibility)
+import Debian.Repo.PackageID (PackageID, makeBinaryPackageID)
+import Debian.Repo.PackageIndex (PackageIndex(PackageIndex))
+import Extra.Email (sendEmails)
+import Debian.Repo.Prelude.GPGSign (PGPKey(Default, Key))
+import Debian.Repo.Prelude.Lock (withLock)
+import Debian.Repo.Prelude.Verbosity (qPutStrLn)
+import Debian.Repo.Release (Release(..), parseArchitectures, parseComponents, releaseName, releaseAliases, releaseComponents, releaseArchitectures)
+import Debian.Repo.State.Package (scanIncoming, deleteSourcePackages, deleteTrumped, deleteBinaryOrphans, deleteGarbage, InstallResult(Ok), explainError, resultToProblems, showErrors, MonadInstall, evalInstall)
+import Debian.Repo.State.Release (findReleases, prepareRelease, writeRelease, signRepo, mergeReleases)
+import Debian.Repo.State.Repository (readLocalRepository, prepareLocalRepository)
+import Debian.Version (parseDebianVersion, prettyDebianVersion)
+import Prelude hiding (putStr, putStrLn, putChar)
+import System.Console.GetOpt (ArgOrder(Permute), getOpt, usageInfo)
+import System.Directory (createDirectoryIfMissing)
+import System.Environment (getArgs)
+import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith)
+import System.FilePath ((</>))
+import System.IO as IO (putStrLn, hFlush, stderr)
+import Text.Regex (mkRegex, splitRegex)
+
+import Config (option)
+import Options (Params(install, rootParam, printVersion, layout, dryRun, binaryOrphans, cleanUp,
+                       sign, notifyEmail, senderEmail, keyName, aliases, sections, architectures,
+                       releases, expire, removePackages), homeParams, optSpecs)
+import Version (myVersion)
+
+main :: IO ()
+main =
+    do args <- getArgs
+       params <- homeParams
+       flags <- case getOpt Permute (map option optSpecs) args of
+                  (o, _n, []) -> return $ foldl (flip id) params o
+                  (_, _, errs) -> error (concat errs ++ usageInfo "Usage:" (map option optSpecs))
+       qPutStrLn ("Flags:\n  " ++ (show flags))
+       let lockPath = rootParam flags </> "newdist.lock"
+       liftIO $ createDirectoryIfMissing True (rootParam flags)
+       case printVersion flags of
+         False -> withLock lockPath (runReposT (runFlags flags))
+         True -> IO.putStrLn myVersion >> exitWith ExitSuccess
+
+-- dry :: Params -> IO () -> IO ()
+-- dry params action = if dryRun params then return () else action
+
+runFlags :: MonadRepos m => Params -> m ()
+runFlags flags =
+    do createReleases flags
+       repo <- readLocalRepository (root flags) (Just . layout $ flags) >>= maybe (error $ "Invalid repository location: " ++ show (outsidePath (root flags))) return
+       evalInstall (do rels <- findReleases repo
+                       _ <- deletePackages (dryRun flags) rels flags keyname
+                       liftIO $ setRepositoryCompatibility repo
+                       when (install flags) $ do
+                         results <- scanIncoming False keyname repo
+                         liftIO $ sendEmails senderAddr emailAddrs (map (email repo) results)
+                         liftIO $ exitOnError results
+                       when (expire flags)  $ deleteTrumped (dryRun flags) keyname rels >> return ()
+                       when (binaryOrphans flags)  $ deleteBinaryOrphans (dryRun flags) keyname rels >> return ()
+                       when (cleanUp flags) $ deleteGarbage >> return ()
+                       when (sign flags) $ liftIO (mapM_ (\ rel -> writeRelease repo rel >>= signRepo keyname repo) rels))
+         repo keyname
+    where
+      emailAddrs :: [(String, String)]
+      emailAddrs =
+          catMaybes . map parseEmail . concat . map (splitRegex (mkRegex "[ \t]*,[ \t]*")) . notifyEmail $ flags
+      senderAddr :: (String, String)
+      senderAddr = maybe ("autobuilder", "somewhere") id . maybe Nothing parseEmail . senderEmail $ flags
+      email :: LocalRepository -> (ChangesFile, InstallResult) -> (String, [String])
+      email repo (changesFile, Ok) =
+          let subject = ("newdist: " ++ changePackage changesFile ++ "-" ++ show (prettyDebianVersion (changeVersion changesFile)) ++
+                         " now available in " ++ releaseName' (changeRelease changesFile) ++
+                         " (" ++ show (prettyArch (changeArch changesFile)) ++")")
+              body = ("Repository " ++ envPath (repoRoot repo)) : [] : (lines $ ppDisplay $ changeInfo changesFile) in
+    	  (subject, body)
+      email _repo (changesFile, e) =
+          let subject = ("newdist failure: " ++ changePackage changesFile ++ "-" ++ show (prettyDebianVersion (changeVersion changesFile)) ++
+                         " failed to install in " ++ releaseName' (changeRelease changesFile))
+              body = concat (map (lines . explainError) (resultToProblems e)) in
+          (subject, body)
+      keyname =
+          case (keyName flags, sign flags) of
+            (Just "none", _) -> Nothing
+            (_, False) -> Nothing
+            (Nothing, True) -> Just {-Debian.Repo.Prelude.GPGSign.-}Default
+            (Just x, True) -> Just ({-Debian.Repo.Prelude.GPGSign.-}Key x)
+      parseEmail s = case break (== '@') s of
+                       (user, ('@' : host)) -> Just (user, host)
+                       _ -> Nothing
+
+-- | Make sure the debian releases which are referenced by the command
+-- line flags exist.
+createReleases :: MonadRepos m => Params -> m ()
+createReleases flags =
+    do let defaultReleases = map (\ name -> Release { releaseName = ReleaseName name
+                                                    , releaseAliases = []
+                                                    , releaseArchitectures = archSet flags
+                                                    , releaseComponents = defaultComponents }) (releases flags)
+       repo <- case defaultReleases of
+                 [] -> readLocalRepository (root flags) (Just . layout $ flags) >>=
+                       maybe (error $ "Invalid repository location: " ++ show (outsidePath (root flags))) return
+                 _ -> prepareLocalRepository (root flags) (Just . layout $ flags) defaultReleases
+       rels <- findReleases repo
+       -- This might already be done
+       mapM_ (createRelease repo (archSet flags)) (map parseReleaseName . releases $ flags)
+       mapM_ (createAlias repo) (aliases flags)
+       mapM_ (createSectionOfRelease repo rels) (sections flags)
+    where
+      createSectionOfRelease repo rels arg =
+          case break (== ',') arg of
+            (rel, ',' : sectName) ->
+                case filter (\ release -> releaseName release == parseReleaseName rel) rels of
+                  [release] -> createSection repo release (parseSection' sectName)
+                  [] -> error $ "createReleases: Invalid release name: " ++ rel
+                  _ -> error "Internal error 1"
+            _ ->
+                error $ "Invalid argument to --create-section: " ++ arg
+      createSection :: MonadRepos m => LocalRepository -> Release -> Section -> m Release
+      createSection repo release section' =
+          case filter ((==) section') (releaseComponents release) of
+            [] -> prepareRelease repo (releaseName release) (releaseAliases release)
+                    (releaseComponents release ++ [section'])  (releaseArchitectures release)
+            _ -> return release
+
+root :: Params -> EnvPath
+root flags = EnvPath (EnvRoot "") (rootParam flags)
+
+archSet :: Params -> Set Arch
+archSet flags = maybe defaultArchitectures (parseArchitectures . pack) $ architectures flags
+
+defaultArchitectures :: Set Arch
+defaultArchitectures = fromList [Binary (ArchOS "linux") (ArchCPU "i386"), Binary (ArchOS "linux") (ArchCPU "amd64")]
+
+defaultComponents :: [Section]
+defaultComponents = parseComponents "main"
+
+createRelease :: MonadRepos m => LocalRepository -> Set Arch -> ReleaseName -> m Release
+createRelease repo archList' name =
+    do rels <- findReleases repo
+       case filter (\ release -> elem name (releaseName release : releaseAliases release)) rels of
+         [] -> prepareRelease repo name [] [parseSection' "main"] archList'
+         [release] -> return release
+         _ -> error "Internal error 2"
+
+createAlias :: MonadRepos m => LocalRepository -> String -> m Release
+createAlias repo arg =
+    case break (== '=') arg of
+      (rel, ('=' : alias)) ->
+          do rels <- findReleases repo
+             case filter ((==) (parseReleaseName rel) . releaseName) rels of
+               [] -> error $ "Attempt to create alias in non-existant release: " ++ rel
+               [release] ->
+                   case elem (parseReleaseName alias) (releaseAliases release) of
+                     False -> prepareRelease repo (parseReleaseName rel) (releaseAliases release ++ [parseReleaseName alias])
+                               (releaseComponents release) (releaseArchitectures release)
+                     True -> return release
+               _ -> error $ "Internal error 3"
+      _ -> error $ "Invalid argument to --create-alias: " ++ arg
+
+exitOnError :: [(ChangesFile, InstallResult)] -> IO ()
+exitOnError results | any (not . (== Ok) . snd) results =
+    do qPutStrLn (showErrors (map snd results))
+       liftIO $ IO.hFlush IO.stderr
+       liftIO $ exitWith (ExitFailure 1)
+exitOnError _ = return ()
+
+-- |Return the list of releases in the repository at root, creating
+-- the ones in the dists list with the given components and
+-- architectures.
+getReleases :: MonadRepos m => EnvPath -> Maybe Layout -> [ReleaseName] -> [Section] -> Set Arch -> m Release
+getReleases root' layout' dists section' archSet' =
+    do repo <- readLocalRepository root' layout' >>= maybe (error $ "Invalid repository location: " ++ show (outsidePath root')) return
+       existingReleases <- findReleases repo
+       requiredReleases <- mapM (\ dist -> prepareRelease repo dist [] section' archSet') dists
+       return $ mergeReleases repo (existingReleases ++ requiredReleases)
+
+deletePackages :: MonadInstall m => Bool -> [Release] -> Params -> Maybe PGPKey -> m [Release]
+deletePackages dry rels flags keyname =
+    deleteSourcePackages dry keyname toRemove
+    where
+      toRemove :: [(Release, PackageIndex, PackageID BinPkgName)]
+      toRemove = map parsePackage $ removePackages flags
+      parsePackage :: String -> (Release, PackageIndex, PackageID BinPkgName)
+      -- Parse a string in the form <dist>,<packagename>=<versionnumber>
+      parsePackage s =
+          case splitRegex (mkRegex "[,=]") s of
+            [dist, component, name, ver] ->
+                maybe (error ("Can't find release: " ++ dist))
+                      (\ release -> (release,
+                                     PackageIndex (parseSection' component) Source,
+                                     makeBinaryPackageID name (parseDebianVersion ver)))
+                      (findReleaseByName (parseReleaseName dist))
+            x -> error ("Invalid remove spec: " ++ show x)
+      findReleaseByName :: ReleaseName -> Maybe Release
+      findReleaseByName dist =
+          case filter (\ rel -> releaseName rel == dist) rels of
+            [] -> Nothing
+            [release] -> (Just release)
+            _ -> error ("Multiple releases with name " ++ releaseName' dist)
diff --git a/testdata/debian/Debian/NewDist/Options.hs b/testdata/debian/Debian/NewDist/Options.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/NewDist/Options.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE TupleSections #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Replacement for debpool.
+module Options
+    ( Params(..)
+    , initialParams
+    , homeParams
+    , optSpecs
+    ) where
+
+import Data.List (intercalate)
+import Debian.Repo.LocalRepository (Layout(..))
+import Prelude hiding (putStr, putStrLn, putChar)
+import System.Console.GetOpt (ArgDescr(NoArg, ReqArg))
+import System.Environment (getEnv)
+
+import Config (ParamDescr(..))
+
+data Params
+    = Params
+      { verbosity :: Int
+      , rootParam :: FilePath
+      , uploadSection :: Maybe FilePath
+      , expire :: Bool
+      , binaryOrphans :: Bool
+      , cleanUp :: Bool
+      , dryRun :: Bool
+      , removePackages :: [String]
+      , install :: Bool
+      , releases :: [String]
+      , aliases :: [String]
+      , sections :: [String]
+      , replace :: Bool
+      , notifyEmail :: [String]
+      , senderEmail :: Maybe String
+      , verify :: Bool
+      , rejectRevision :: Maybe String
+      , printVersion :: Bool
+      , layout :: Layout
+      , sign :: Bool
+      , architectures :: Maybe String
+      , keyName :: Maybe String -- "Name of the pgp key with which to sign the repository."
+      } deriving Show
+
+initialParams :: Params
+initialParams =
+    Params
+    { verbosity = 0
+    , rootParam = "."
+    , uploadSection = Nothing
+    , expire = False
+    , binaryOrphans = False
+    , cleanUp = False
+    , dryRun = False
+    , removePackages = []
+    , install = True
+    , releases = []
+    , aliases = []
+    , sections = []
+    , replace = False
+    , notifyEmail = []
+    , senderEmail = Nothing
+    , verify = False
+    , rejectRevision = Nothing
+    , printVersion = False
+    , layout = Pool
+    , sign = True
+    , architectures = Nothing
+    , keyName = Nothing
+    }
+
+homeParams :: IO Params
+homeParams = getEnv "HOME" >>= \ home -> return $ initialParams {rootParam = home}
+
+optSpecs :: [ParamDescr (Params -> Params)]
+optSpecs =
+    [ Param ['v'] ["verbose"] ["Verbose"] (NoArg (\ p -> p {verbosity = verbosity p + 1}))
+                 "Increase the amount of debugging output"
+    , Param [] ["root"] ["Root"] (ReqArg (\ s p -> p {rootParam = s}) "PATH")
+		 "Specify the root directory of the repository"
+    , Param [] ["section"] ["Section"] (ReqArg (\ s p -> p {uploadSection = Just s}) "PATH")
+		 "Force uploads to the specified section"
+    , Param [] ["expire"] ["Expire"] (NoArg (\ p -> p {expire = True}))
+		 "Remove all packages trumped by newer versions from the package lists."
+    , Param [] ["binary-orphans"] ["Binary-Orphans"] (NoArg (\ p -> p {binaryOrphans = True}))
+		 "Remove all binary packages that have no corresponding source package."
+    , Param [] ["clean-up"] ["Clean-Up"] (NoArg (\ p -> p {cleanUp = True}))
+		 "Move all unreferenced files in the repository to the removed directory."
+    , Param ['n'] ["dry-run"] ["Dry-Run"] (NoArg (\ p -> p {dryRun = True})) "Test run, don't modify the repository.  (ONLY IMPLEMENTED FOR REMOVE)"
+    , Param [] ["remove"] ["Remove"] (ReqArg (\ s p -> p {removePackages = removePackages p ++ [s]}) "DIST,SECTION,PACKAGE=VERSION")
+                 "remove a particular version of a package (may be repeated.)"
+    , Param ['i'] ["install"] ["Install"] (NoArg (\ p -> p {install = True}))
+                 ("Scan the incoming directory and install the packages found there.\n" ++
+                  "This option is automatically true if no --remove arguments are given.")
+    , Param [] ["create-release"] ["Create-Release"] (ReqArg (\ s p -> p {releases = releases p ++ [s]}) "NAME")
+		 ("Create any new releases and/or sections found in Distribution\n" ++
+                  "and Section fields of the uploaded .changes files.")
+    , Param [] ["create-alias"] ["Create-Alias"] (ReqArg (\ s p -> p {aliases = aliases p ++ [s]}) "ALIAS=RELEASE")
+		 "Create an alias for an existing release"
+    , Param [] ["create-section"] ["Create-Section"] (ReqArg (\ s p -> p {sections = sections p ++ [s]}) "RELEASE,SECTION")
+		 "Create a new section in the given release."
+    , Param [] ["replace"] ["Replace"]	(NoArg (\ p -> p {replace = True}))
+                 ("Permit uploading of a package whose version is already present.\n" ++
+                  "This is normally an error.")
+    , Param [] ["notify-email"] ["Notify-Email"] (ReqArg (\ s p -> p {notifyEmail = notifyEmail p ++ [s]}) "USER@HOST")
+                 "Email address to send notifications about success and failure of uploads."
+    , Param [] ["sender-email"] ["Sender-Email"] (ReqArg (\ s p -> p {senderEmail = Just s}) "USER@HOST")
+                 "Sender address for notifications."
+    , Param [] ["verify"] ["Verify"] (NoArg (\ p -> p {verify = True}))
+                "Verify the structure and contents of the repository."
+    , Param [] ["reject-revision"] ["Reject-Revision"] (ReqArg (\ s p -> p {rejectRevision = Just s}) "STRING")
+                 ("Disallow uploads of packages with this revision string.  The\n" ++
+                  "autobuilder gives 'dir' targets the revision string 'none', the\n" ++
+                  "option 'Reject-Revision: none' can be used to prohibit uploads of\n" ++
+                  "packages built from a 'dir' target.")
+    , Param [] ["version"] ["Version"] (NoArg (\ p -> p {printVersion = True}))
+                 "Print the version string and exit"
+    , Param [] ["layout"] ["Layout"] (ReqArg (\ s p -> p {layout = read s}) (intercalate " or " (map show ([minBound .. maxBound] :: [Layout])) ++ ", default " ++ show (layout initialParams)))
+                 "Specify a default layout"
+    , Param [] ["no-sign"] ["No-Sign"] (NoArg (\ p -> p {sign = False}))
+                 "Do not attempt to cryptographically sign the repository, even if something changed."
+    , Param [] ["sign"] ["Sign"] (NoArg (\ p -> p {sign = True}))
+                 "Cryptographically sign the repository, even if nothing changed."
+    , Param [] ["architectures"] ["Architectures"] (ReqArg (\ s p -> p {architectures = Just s}) "ARCH,ARCH,...")
+                 ""
+    , Param [] ["keyname"] ["Key-Name"] (ReqArg (\ s p -> p {keyName = Just s}) "STRING")
+                 "Name of the pgp key with which to sign the repository."
+{-
+     , Param [] ["rebuild"] ["Rebuild"]	(NoArg (Value "Rebuild" "yes"))
+     "(UNIMPLEMENTED) Reconstruct the package lists from scratch."
+     , Param [] ["obsolete"] ["Obsolete"]	(NoArg (Value "Obsolete" "yes"))
+     (My.consperse "\n"
+      ["(UNIMPLEMENTED) Remove any package for which newer versions exist,",
+       "remove any package which is not part of any dist."])
+-}
+    ]
diff --git a/testdata/debian/Debian/NewDist/Version.hs b/testdata/debian/Debian/NewDist/Version.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/NewDist/Version.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE CPP, TemplateHaskell #-}
+module Version (myVersion) where
+
+import Data.Version (showVersion)
+import Paths_debian_repo (version)
+
+myVersion :: String
+myVersion = showVersion version
diff --git a/testdata/debian/Debian/Repo.hs b/testdata/debian/Debian/Repo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo.hs
@@ -0,0 +1,52 @@
+-- |This is a set of modules tied together by the AptIO monad in
+-- IO.hs, which keeps track of the contents of all the Apt
+-- repositories which are queried in the course of execution.
+module Debian.Repo
+    ( module Debian.Repo.AptImage
+    , module Debian.Repo.Changes
+    , module Debian.Repo.Dependencies
+    , module Debian.Repo.EnvPath
+    , module Debian.Repo.Internal.Repos
+    , module Debian.Repo.LocalRepository
+    , module Debian.Repo.OSImage
+    , module Debian.Repo.Package
+    , module Debian.Repo.PackageID
+    , module Debian.Repo.PackageIndex
+    , module Debian.Repo.Prelude
+    , module Debian.Repo.Release
+    , module Debian.Repo.RemoteRepository
+    , module Debian.Repo.Repo
+    , module Debian.Repo.Slice
+    , module Debian.Repo.SourceTree
+    , module Debian.Repo.State.AptImage
+    , module Debian.Repo.State.OSImage
+    , module Debian.Repo.State.Package
+    , module Debian.Repo.State.PackageIndex
+    , module Debian.Repo.State.Release
+    , module Debian.Repo.State.Slice
+    , module Debian.Repo.Top
+    ) where
+
+import Debian.Repo.AptImage
+import Debian.Repo.Changes
+import Debian.Repo.Dependencies
+import Debian.Repo.EnvPath
+import Debian.Repo.Internal.Repos (MonadRepos)
+import Debian.Repo.LocalRepository
+import Debian.Repo.OSImage
+import Debian.Repo.Package
+import Debian.Repo.PackageID
+import Debian.Repo.PackageIndex
+import Debian.Repo.Prelude
+import Debian.Repo.Release
+import Debian.Repo.RemoteRepository
+import Debian.Repo.Repo
+import Debian.Repo.Slice
+import Debian.Repo.SourceTree
+import Debian.Repo.State.AptImage
+import Debian.Repo.State.OSImage
+import Debian.Repo.State.Package
+import Debian.Repo.State.PackageIndex
+import Debian.Repo.State.Release
+import Debian.Repo.State.Slice
+import Debian.Repo.Top
diff --git a/testdata/debian/Debian/Repo/AptImage.hs b/testdata/debian/Debian/Repo/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/AptImage.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings,
+             PackageImports, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.AptImage
+    ( aptDir
+    , buildArchOfRoot
+    , aptGetSource
+    , aptGetUpdate
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Category ((.))
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.Data (Data)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..))
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (PkgName, SrcPkgName(unSrcPkgName))
+import Debian.Repo.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.Internal.Apt (AptImage(aptImageRoot, aptImageSources), MonadApt(getApt))
+import Debian.Repo.Prelude.Verbosity (readProcFailing)
+import Debian.Repo.Slice (NamedSliceList(sliceListName))
+import Debian.Repo.Top (distDir, MonadTop)
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Prelude hiding ((.))
+import System.Directory (createDirectoryIfMissing)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.Process (CreateProcess(cwd), proc, readProcessWithExitCode)
+
+-- | The location of the top directory of a source packages's files in
+-- an AptImage (but not an OSImage.)
+aptDir :: (MonadTop m, MonadApt m) => SrcPkgName -> m FilePath
+aptDir package =
+    do rel <- aptImageSources <$> getApt
+       dir <- distDir (sliceListName rel)
+       return $ dir </> "apt" </> unSrcPkgName package
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- | Run an apt-get command in a particular directory with a
+-- particular list of packages.  Note that apt-get source works for
+-- binary or source package names.
+aptGetSource :: (MonadIO m, MonadApt m, PkgName n) => FilePath -> [(n, Maybe DebianVersion)] -> m ()
+aptGetSource dir packages =
+    do args <- aptOpts
+       let p = (proc "apt-get" (args ++ ["source"] ++ map formatPackage packages)) {cwd = Just dir}
+       liftIO $ createDirectoryIfMissing True dir >> readProcFailing p "" >> return ()
+    where
+      formatPackage (name, Nothing) = ppDisplay name
+      formatPackage (name, Just version) = ppDisplay name ++ "=" ++ show (prettyDebianVersion version)
+
+aptGetUpdate :: (MonadIO m, MonadApt m) => m ()
+aptGetUpdate =
+    do args <- aptOpts
+       let p = (proc "apt-get" (args ++ ["update"]))
+       _ <- liftIO $ readProcFailing p ""
+       return ()
+
+aptOpts :: MonadApt m => m [String]
+aptOpts =
+    do root <- (rootPath . aptImageRoot) <$> getApt
+       return $ [ "-o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status"
+                , "-o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists"
+                , "-o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives"
+                , "-o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list"
+                , "-o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d" ]
diff --git a/testdata/debian/Debian/Repo/Changes.hs b/testdata/debian/Debian/Repo/Changes.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Changes.hs
@@ -0,0 +1,162 @@
+-- | A .changes file is a file generated by dpkg-buildpackage
+-- describing the result of a package build.  Not to be confused
+-- with a debian/changelog file.  After it is uploaded it goes into
+-- the @installed/@ subdirectory of the repository.
+{-# LANGUAGE PackageImports, OverloadedStrings #-}
+{-# OPTIONS -fno-warn-name-shadowing -fno-warn-missing-signatures #-}
+module Debian.Repo.Changes
+    ( findChangesFiles
+    , saveChangesFile
+{-
+    , parseChangesFilename
+    , parseChangesFile
+-}
+    , changeKey
+    , changePath
+    , changeName
+    ) where
+
+import Data.List (isSuffixOf)
+import Data.Maybe (catMaybes)
+import Data.Monoid ((<>), mconcat)
+import Data.Text (pack, Text, unpack)
+import Debian.Arch (Arch, parseArch, prettyArch)
+import Debian.Changes (ChangedFileSpec(..), ChangesFile(..), changesFileName, parseChanges)
+import qualified Debian.Control.Text as S (Control'(Control), ControlFunctions(parseControlFromFile), fieldValue, modifyField, Paragraph'(..))
+import Debian.Pretty (ppDisplay)
+import Debian.Release (parseReleaseName, parseSection)
+import Debian.Repo.Prelude (replaceFile)
+import Debian.URI ()
+import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)
+import System.Directory (getDirectoryContents)
+import Text.Regex (matchRegex, matchRegexAll, mkRegex)
+
+findChangesFiles :: FilePath -> IO [ChangesFile]
+findChangesFiles dir =
+    getDirectoryContents dir >>=
+    return . filter (isSuffixOf ".changes") >>=
+    mapM (loadChangesFile dir) >>= return . catMaybes
+
+loadChangesFile :: FilePath -> String -> IO (Maybe ChangesFile)
+loadChangesFile dir file =
+    do
+      case parseChangesFilename file of
+        Just (name, ver, arch) ->
+            do
+              result <- parseChangesFile dir file
+              case result of
+                Right (S.Control changes) ->
+                    -- The .changes file should be a single paragraph,
+                    -- but there have been instances where extra newlines
+                    -- are inserted.  To be forgiving we will concat all
+                    -- the paragraphs into one (rather than erroring out
+                    -- or discarding all but the first paragraph.)
+                    let changes' = mergeParagraphs changes in
+                    case (S.fieldValue "Files" changes' :: Maybe Text,
+                          S.fieldValue "Checksums-Sha1" changes' :: Maybe Text,
+                          S.fieldValue "Checksums-Sha256" changes' :: Maybe Text,
+                          maybe Nothing parseChanges (S.fieldValue "Changes" changes'),
+                          S.fieldValue "Distribution" changes') of
+                      (Just text, sha1text, sha256text, Just entry, Just release) ->
+                          do return . Just $ Changes { changeDir = dir
+                                                     , changePackage = name
+                                                     , changeVersion = ver
+                                                     , changeRelease = parseReleaseName (unpack release)
+                                                     , changeArch = arch
+                                                     , changeInfo = changes'
+                                                     , changeEntry = entry
+                                                     , changeFiles = changedFileSpecs text sha1text sha256text }
+                      _ -> return Nothing	-- Missing 'Files', 'Checksums-Sha1', 'Checksums-Sha256', 'Changes', or 'Distribution' field in .changes
+                Left _error -> return Nothing
+        Nothing -> return Nothing		-- Couldn't parse changes filename
+
+mergeParagraphs :: [S.Paragraph' Text] -> S.Paragraph' Text
+mergeParagraphs paragraphs =
+    S.Paragraph . concat . map fieldsOf $ paragraphs
+    where fieldsOf (S.Paragraph fields) = fields
+
+saveChangesFile :: ChangesFile -> IO ()
+saveChangesFile changes =
+    replaceFile path (ppDisplay (updateFiles (changeFiles changes) (changeInfo changes)))
+    where
+      path = changeDir changes ++ "/" ++ changesFileName changes
+      updateFiles files info =
+          S.modifyField "Checksums-Sha1" (const (showSHA1List files)).
+          S.modifyField "Checksums-Sha256" (const (showSHA256List files)) .
+          S.modifyField "Files" (const (showFileList files)) $ info
+
+-- | They primary key for .changes files is the triple containing the
+-- package name, version, and architecture.  There is a 1-1 correspondence
+-- between a key value and the name of the .changes file.
+changeKey :: ChangesFile -> (String, DebianVersion, Arch)
+changeKey changes = (changePackage changes, changeVersion changes, changeArch changes)
+
+changeName :: ChangesFile -> FilePath
+changeName changes =
+    changePackage changes <> "_" <>
+    show (prettyDebianVersion (changeVersion changes)) <> "_" <>
+    show (prettyArch (changeArch changes)) <> ".changes"
+
+changePath :: ChangesFile -> FilePath
+changePath changes = changeDir changes ++ "/" ++ changeName changes
+
+-- | I suspect it would be more correct to read the contents of the file and
+-- use that to construct the info we are inferring here from the filename.
+parseChangesFilename :: String -> Maybe (String, DebianVersion, Arch)
+parseChangesFilename name =
+    case matchRegex (mkRegex "^(.*/)?([^_]*)_(.*)_([^.]*)\\.changes$") name of
+      Just [_, name, version, arch] -> Just (name, parseDebianVersion version, parseArch arch)
+      _ -> error ("Invalid .changes file name: " ++ name)
+
+-- parseChangesFile :: FilePath -> String -> IO (Either ParseError S.Control)
+parseChangesFile dir file = S.parseControlFromFile (dir ++ "/" ++ file)
+
+changedFileSpecs :: Text -> Maybe Text -> Maybe Text -> [ChangedFileSpec]
+changedFileSpecs text sha1text sha256text =
+    map changedFileSpec fileInfo
+    where
+      changedFileSpec (md5sum, size, section, priority, filename) =
+                ChangedFileSpec { changedFileMD5sum = md5sum
+                                , changedFileSHA1sum = maybe "" fst (lookup filename sha1sums)
+                                , changedFileSHA256sum = maybe "" fst (lookup filename sha256sums)
+                                , changedFileSize = read size
+                                , changedFileSection = parseSection section
+                                , changedFilePriority = priority
+                                , changedFileName = filename }
+      fileInfo = parseFileList (unpack text)
+      sha1sums = maybe [] (parseChecksums . unpack) sha1text
+      sha256sums = maybe [] (parseChecksums . unpack) sha256text
+
+parseFileList :: String -> [(String, String, String, String, String)]
+parseFileList text =
+    case matchRegexAll re text of
+      Just (_, _, remaining, [md5sum, size, section, priority, filename]) ->
+          (md5sum, size, section, priority, filename) : parseFileList remaining
+      _ -> []
+    where
+      re = mkRegex ("^[ \t\n]*" ++ g ++w++ g ++w++ g ++w++ g ++w++ g ++ "[ \t\n]*")
+      g = "(" ++ t ++ ")"
+      t = "[^ \t\n]+"
+      w = "[ \t]+"
+
+parseChecksums :: String -> [(String, (String, String))]
+parseChecksums text =
+    case matchRegexAll re text of
+      Just (_, _, remaining, [checksum, size, name]) -> (name, (checksum, size)) : parseChecksums remaining
+      _ -> []
+    where
+      re = mkRegex ("^[ \t\n]*" ++ g ++w++ g ++w++ g ++ "[ \t\n]*")
+      g = "(" ++ t ++ ")"
+      t = "[^ \t\n]+"
+      w = "[ \t]+"
+
+showFileList :: [ChangedFileSpec] -> Text
+showFileList files = pack $ concat (map (("\n " <>) . ppDisplay) files)
+
+showSHA1List :: [ChangedFileSpec] -> Text
+showSHA1List files = mconcat (map (("\n " <>) . showSHA1) files)
+    where showSHA1 x = pack $ changedFileSHA1sum x ++ " " ++ show (changedFileSize x) ++ " " ++ changedFileName x
+
+showSHA256List :: [ChangedFileSpec] -> Text
+showSHA256List files = mconcat (map (("\n " <>) . showSHA256) files)
+    where showSHA256 x = pack $ changedFileSHA256sum x ++ " " ++ show (changedFileSize x) ++ " " ++ changedFileName x
diff --git a/testdata/debian/Debian/Repo/Dependencies.hs b/testdata/debian/Debian/Repo/Dependencies.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Dependencies.hs
@@ -0,0 +1,246 @@
+-- | This dependency solver determines which binary packages to install
+-- in order to satisfy a set of dependency relations.  It uses a brute
+-- force method, but tweaked to the point where it is usually able to
+-- complete on real-world inputs.
+--
+-- Author: David Fox <dsf@seereason.com>
+{-# LANGUAGE OverloadedStrings, PackageImports, ScopedTypeVariables, ScopedTypeVariables #-}
+{-# OPTIONS -Wall -fwarn-missing-signatures #-}
+module Debian.Repo.Dependencies
+    ( simplifyRelations
+    , solutions
+    , testArch
+    , prettySimpleRelation
+    , showSimpleRelation
+    , readSimpleRelation
+    ) where
+
+import Control.Arrow (second)
+import Data.List (groupBy, intercalate, nub, sortBy)
+import qualified Data.Map as Map (empty, findWithDefault, fromListWith, insert, lookup, Map)
+import Data.Maybe (catMaybes)
+import Data.Monoid ((<>))
+import Data.Set (toList)
+import qualified Data.Set as Set (Set, singleton, toList, union)
+import Debian.Arch (Arch(Source, Binary), ArchCPU(..), ArchOS(..))
+import Debian.Control ()
+import qualified Debian.Control.Text as S ()
+import Debian.Pretty (ppPrint)
+import Debian.Relation (ArchitectureReq(..), BinPkgName(BinPkgName), Relation(..), Relations, VersionReq(..))
+import Debian.Repo.PackageID (PackageID(PackageID, packageName, packageVersion))
+import Debian.Repo.PackageIndex (BinaryPackage, packageID, pProvides)
+import Debian.Repo.Prelude (cartesianProduct)
+import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)
+import Text.PrettyPrint.HughesPJClass (Doc, text)
+
+type Excuse = String
+
+type ProvidesMap = Map.Map BinPkgName [BinaryPackage]
+
+-- |A SimpleRelation just specifies a particular version of a package.
+-- The Nothing relation is always satisified.
+type SimpleRelation = Maybe (PackageID BinPkgName)
+
+prettySimpleRelation :: SimpleRelation -> Doc
+prettySimpleRelation Nothing = text "Nothing"
+prettySimpleRelation (Just p) = ppPrint (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+showSimpleRelation :: PackageID BinPkgName -> String
+showSimpleRelation v = show (prettySimpleRelation (Just v))
+
+-- Shouldn't this return a maybe?
+readSimpleRelation :: String -> PackageID BinPkgName
+readSimpleRelation s = case second (parseDebianVersion . (drop 1)) (span (/= '=') s) of
+                         (n, v) -> PackageID { packageName = BinPkgName n, packageVersion = v }
+
+-- |Each element is an or-list of specific package versions.
+type SimpleRelations = [[SimpleRelation]]
+
+-- Does this relation apply to this architecture?
+testArch :: Arch -> Relation -> Bool
+testArch _ (Rel _ _ Nothing) = True
+testArch architecture (Rel _ _ (Just (ArchOnly archList))) = any (testArch' architecture) (toList archList)
+testArch architecture (Rel _ _ (Just (ArchExcept archList))) = not (any (testArch' architecture) (toList archList))
+
+testArch' :: Arch -> Arch -> Bool
+testArch' (Binary os1 cpu1) (Binary os2 cpu2) = testOS os1 os2 && testCPU cpu1 cpu2
+testArch' Source Source = True
+testArch' _ _ = False
+
+testOS :: ArchOS -> ArchOS -> Bool
+testOS _ ArchOSAny = True
+testOS (ArchOS a) (ArchOS b) = a == b
+testOS ArchOSAny _ = error "testOS - invalid argument"
+
+testCPU :: ArchCPU -> ArchCPU -> Bool
+testCPU _ ArchCPUAny = True
+testCPU (ArchCPU a) (ArchCPU b) = a == b
+testCPU ArchCPUAny _ = error "testCPU - invalid argument"
+
+-- |Turn the expressive inequality style relations to a set of simple
+-- equality relations on only the packages in the available list.
+simplifyRelations :: [BinaryPackage]
+                  -> Relations
+                  -> [BinPkgName]	-- ^ Given several alternative packages which satisfy
+				        -- the relation, sort by name in this order.
+                  -> Arch		-- ^ The build architecture
+                  -> SimpleRelations
+simplifyRelations available relations preferred arch =
+    -- Sort the or-relations so that
+    --  1. the packages named in the preferred list appear before other packages,
+    --  2. the newest package appears first
+    -- But we need to preserve the order that packages with different names appear
+    map (sortBy prefOrder . sortBy versionOrder) relationsSimplified
+    where
+      relationsSimplified = expandVirtual arch nameMap providesMap relationsOfArch
+          where
+            nameMap = listMap (map (\ package -> (packageName (packageID package), package)) available)
+            providesMap =
+                listMap (concat (map (\ package -> 
+                                      let names = packageName (packageID package) : map provides (pProvides package) in
+                                      map (\ name -> (name, package)) names) available))
+            provides [Rel name Nothing Nothing] = name
+            provides bzzt = error ("Invalid relation in Provides: " ++ show (map ppPrint bzzt))
+            relationsOfArch = filter (/= []) (map (nub . filter (testArch arch)) relations)
+      prefOrder a b = compare (isPreferred a) (isPreferred b)
+          where isPreferred = maybe False (flip elem preferred . packageName)
+      versionOrder :: SimpleRelation -> SimpleRelation -> Ordering
+      versionOrder (Just a) (Just b) | packageName a /= packageName b = EQ
+      versionOrder (Just a) (Just b) = compare (packageVersion b) (packageVersion a)
+      versionOrder _ _ = EQ
+
+-- |Replace all relations by sets of equality relations on the exact
+-- package versions which are actually available in the current
+-- environment and satisfy the original relation.
+expandVirtual :: Arch -> ProvidesMap -> ProvidesMap -> Relations -> SimpleRelations
+expandVirtual Source _ _ _ = undefined
+expandVirtual arch nameMap providesMap relations =
+    map (nub . concat . map expand) relations
+    where
+      -- A relation with no version or architecture requirement
+      -- can be satisfied by a provides or a real package.
+      expand :: Relation -> [SimpleRelation]
+      -- If the relation only applies to other architectures it can be ignored.
+      expand (Rel _ _ (Just (ArchOnly archList))) | not (any (testArch' arch) (toList archList)) = [Nothing]
+      expand (Rel _ _ (Just (ArchExcept archList))) | any (testArch' arch) (toList archList) = [Nothing]
+      expand (Rel name Nothing Nothing) = map eqRel (Map.findWithDefault [] name providesMap)
+      expand rel@(Rel name _ _) = map eqRel (filter (satisfies rel . packageID) (Map.findWithDefault [] name nameMap))
+      eqRel :: BinaryPackage -> SimpleRelation
+      eqRel package =
+          Just (PackageID {packageName = packageName (packageID package), packageVersion = packageVersion p})
+          where p = packageID package
+      -- Does this package satisfy the relation?
+      satisfies :: Relation -> PackageID BinPkgName -> Bool
+      satisfies rel pkg = testRel (packageVersion pkg) rel
+
+-- Does this version satisfy the relation?
+testRel :: DebianVersion -> Relation -> Bool
+testRel _ (Rel _ Nothing _) = True
+testRel ver1 (Rel _ (Just (LTE ver2)) _) = not (compare ver1 ver2 == GT)
+testRel ver1 (Rel _ (Just (GRE ver2)) _) = not (compare ver1 ver2 == LT)
+testRel ver1 (Rel _ (Just (SLT ver2)) _) = compare ver1 ver2 == LT
+testRel ver1 (Rel _ (Just (EEQ ver2)) _) = compare ver1 ver2 == EQ
+testRel ver1 (Rel _ (Just (SGR ver2)) _) = compare ver1 ver2 == GT
+
+-- |Given a root and a dependency list, return a list of possible
+-- solutions to the dependency set.  Each solution is a list of
+-- package versions which satisfy all the dependencies.  Note that if
+-- a package is mentioned in two different clauses of the dependency
+-- list, both clauses must be satisfied:
+--
+-- * a (>= 3.0), a (<< 4.0) | b (>= 2.0), c (>= 1.0) becomes
+--
+-- * a (>= 3.0), a (<< 4.0), c (>= 1.0) OR a (>= 3.0), b (>= 2.0), c (>= 1.0)
+--
+-- * [[a (>= 3.0)], [a (<< 4.0), b (>= 2.0)], [c (>= 1.0)]] becomes
+--
+-- * [[a (>= 3.0), a (<< 4.0), c (>= 1.0)], [a (>= 3.0), b (>= 2.0), c (>= 1.0)]]
+--
+-- So we can use each clause to eliminate packages which cannot
+-- satisfy the dependency set.
+solutions :: [BinaryPackage]	-- ^ The packages available to satisfy dependencies
+          -> SimpleRelations	-- ^ The dependency relations to be satisfied
+          -> Int		-- ^ Give up after this many solutions are computed
+          -> (Either String [(Int, [BinaryPackage])])
+				-- ^ On success return the set of packages to install,
+				-- and the solution's sequence number.  Also returns
+				-- the modified list of dependency relations, with all
+				-- inequalities replaced by equalities on the particular
+				-- versions of each package which are available.
+solutions available relations limit =
+    -- Do any of the dependencies require packages that simply don't
+    -- exist?  If so we don't have to search for solutions, there
+    -- aren't any.
+    case any (== []) relations of
+      True -> Left "Unsatisfiable dependencies"
+      False ->
+          -- Turn the And-of-Ors dependency list into Or-of-And-of-Ands.
+          -- Each element of the result represents a an alternative set of
+          -- constraints which a solution must satisfy.  Each element of
+          -- the alternative is a list of relations on a single package,
+          -- all of which must be satisfied.
+          let alternatives = map (map nub . groupByName) (cartesianProduct relations) in
+          --let versions = map makeVersion available in
+          -- Find a set of packages that satisfies the dependencies
+          case solutions' 1 alternatives available' of
+            -- Add more information about the failure to the error message.
+            Left message ->
+                let results = map (testAlternative available') (take 20 alternatives) in
+                let (errors :: [String]) = catMaybes (map (either Just (const Nothing)) results) in
+                Left (message ++ "\n" ++ intercalate "\n" errors)
+            Right x -> Right x
+    where
+      available' = availMap available
+      solutions' :: Int -> [[[SimpleRelation]]] -> AvailMap (BinaryPackage) -> Either String [(Int, [BinaryPackage])]
+      solutions' _ [] _ = Left "All candidate solutions failed"
+      solutions' count (alternative : alternatives) available'' =
+          if count > limit
+          then Left ("No solutions found in first " ++ show limit ++ " candidates")
+          else case testAlternative available'' alternative of
+                 Left _excuse ->
+                     solutions' (count + 1) alternatives available''
+                 Right solution ->
+                     Right ((count, solution) : either (const []) id (solutions' (count + 1) alternatives available''))
+
+-- |The alternative argument is a possible solution to the dependency
+-- problem.  Each element of alternative represents the relations on a
+-- particular package.  So each one needs to be satisfied for the
+-- alternative to be satisfied.
+testAlternative :: AvailMap (BinaryPackage) -> [[SimpleRelation]] -> Either Excuse [BinaryPackage]
+testAlternative available alternative =
+    if all (not . null) solution then
+        Right (map head solution) else
+        Left ("Couldn't satisfy these conditions:\n  " ++
+              intercalate "\n  " (map (show . map prettySimpleRelation) (mask (map null solution) alternative))
+              {- ++ " using the available packages: " ++ show available -})
+    where
+      -- solution :: [a]
+      solution = map (testPackage available) alternative
+      mask bits elems = map fst (filter snd (zip elems bits))
+
+-- |Return the list of versions of a package that satisfy all of the
+-- relations.
+testPackage :: AvailMap (BinaryPackage) -> [SimpleRelation] -> [BinaryPackage]
+testPackage available rels =
+    foldl satisfies [] rels
+    where
+      -- Which of these packages satisfy the relation?
+      satisfies versions Nothing = versions
+      satisfies versions (Just pkg) =
+          versions ++ maybe [] (filter (\ x -> packageVersion (packageID x) == packageVersion pkg) . Set.toList) (Map.lookup (packageName pkg) available)
+
+groupByName :: [SimpleRelation] -> [[SimpleRelation]]
+groupByName relations =
+    groupBy (\ a b -> compareNames a b == EQ) (sortBy compareNames relations)
+    where compareNames a b = compare (maybe Nothing (Just . packageName) a) (maybe Nothing (Just . packageName) b)
+
+-- Turn a list of (k, a) pairs into a map from k -> [a].
+listMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
+listMap pairs =
+    foldl insertPair Map.empty pairs
+    where insertPair m (k,a) = Map.insert k (a : (Map.findWithDefault [] k m)) m
+
+type AvailMap a = Map.Map BinPkgName (Set.Set a)
+
+availMap :: [BinaryPackage] -> AvailMap BinaryPackage
+availMap xs = Map.fromListWith Set.union (map (\ x -> (packageName (packageID x), Set.singleton x)) xs)
diff --git a/testdata/debian/Debian/Repo/EnvPath.hs b/testdata/debian/Debian/Repo/EnvPath.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/EnvPath.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.EnvPath
+    ( EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
diff --git a/testdata/debian/Debian/Repo/Fingerprint.hs b/testdata/debian/Debian/Repo/Fingerprint.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Fingerprint.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
+module Debian.Repo.Fingerprint
+    ( RetrieveMethod(..)
+    , RetrieveAttribute(..)
+    , GitSpec(..)
+    , Fingerprint(..)
+    , retrieveMethodMD5
+    , readUpstreamFingerprint
+    , DownstreamFingerprint(..)
+    , packageFingerprint
+    , showFingerprint
+    , dependencyChanges
+    , showDependencies
+    , showDependencies'
+    ) where
+
+import Control.Applicative.Error (maybeRead)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Data (Data)
+import Data.Digest.Pure.MD5 (md5)
+import Data.List as List (intercalate, map)
+import Data.Set as Set (Set, toList, toAscList, difference, empty, fromList, map, filter)
+import Data.Text (unpack, strip)
+import Data.Typeable (Typeable)
+import qualified Debian.Control.String as S
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Dependencies (readSimpleRelation, showSimpleRelation)
+import Debian.Repo.PackageID (PackageID(packageName, packageVersion))
+import Debian.Repo.PackageIndex (SourcePackage(sourceParagraph, sourcePackageID))
+import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)
+import Extra.Misc(columns)
+
+-- | The methods we know for obtaining source code.
+data RetrieveMethod
+    = Apt String String                      -- ^ Apt dist name - download using apt-get (FIXME: Apt String SrcPkgName would be better, but that breaks read/show)
+    | Bzr String                             -- ^ Download from a Bazaar repository
+    | Cd FilePath RetrieveMethod             -- ^ Get the source code from a subdirectory of another download
+    | Darcs String                           -- ^ Download from a Darcs repository
+    | DataFiles RetrieveMethod RetrieveMethod FilePath
+                                             -- ^ The first tree is a cabal package, copy the files in the second tree into
+                                             -- the first at the location specified by FilePath.  Typically you would then patch
+                                             -- the cabal file to add entries to the Data-Files list.
+    | DebDir RetrieveMethod RetrieveMethod   -- ^ Combine the upstream download with a download for a debian directory
+    | Debianize'' RetrieveMethod (Maybe String) -- ^ Retrieve a cabal package from Hackage and use cabal-debian to
+                                              -- debianize it.  The optional string is used as the debian source
+                                              -- package name if provided, this allows us to build several versions of
+                                              -- the same source package - e.g. one with ghc, one with ghcjs.
+                                              -- Remember we can't build more than one package with a given source
+                                              -- package name, so this is the only attribute that needs to be part of
+                                              -- the RetrieveMethod for a Debianization.  If not supplied the name is
+                                              -- derived from the cabal package name.
+    | Debianize RetrieveMethod               -- ^ Old Debianize constructor retained for backwards compatibility.
+                                             -- we read this value from the old source packages.
+    | Debianize' RetrieveMethod [()]         -- ^ Another old Debianize constructor
+    | Dir FilePath                           -- ^ Retrieve the source code from a directory on a local machine
+    | Git String [GitSpec]                   -- ^ Download from a Git repository, optional commit hashes and/or branch names
+    | Hackage String                         -- ^ Download a cabal package from hackage
+    | Hg String                              -- ^ Download from a Mercurial repository
+    | Patch RetrieveMethod ByteString        -- ^ Apply the patch given in the string text to the target
+    | Proc RetrieveMethod                    -- ^ Mount proc during the build (this should be a PackageFlag.)
+    | Quilt RetrieveMethod RetrieveMethod    -- ^ Combine a download with a download of a patch directory to be applied by quilt
+    | SourceDeb RetrieveMethod               -- ^ Download and unpack a source deb - a .dsc, a .tar.gz, and a .diff.gz file.
+    | Svn String                             -- ^ Download from a Subversion repository
+    | Tla String                             -- ^ Download from a TLA repository
+    | Twice RetrieveMethod                   -- ^ Perform the build twice (should be a package flag)
+    | Uri String String                      -- ^ Download a tarball from the URI.  The checksum is used to implement caching.
+    | Zero                                   -- ^ Nothing, do not build
+    deriving (Read, Show, Data, Typeable)
+
+data GitSpec
+    = Branch String
+    | Commit String
+    deriving (Read, Show, Eq, Data, Typeable)
+
+-- | Information gathered as a result of the retrieveal.  If these
+-- identifying characteristic of the source tree change between one
+-- retrieve and the next, a build should be triggered.  Examples
+-- include the latest Git commit identifier, or a darcs repository
+-- tag.  A darcs commit string is not a suitable identifier because
+-- darcs commits are not totally ordered, so it can't reliably be used
+-- to reconstruct the specific source tree.  Instead we checksum the
+-- entire darcs change history.
+data RetrieveAttribute
+    = AptVersion String
+    -- ^ The version number of a package retrieved by apt-get source
+    | GitCommit String
+    -- ^ The id of the most recent commit
+    | DarcsChangesId String
+    -- ^ The checksum of the output of darcs changes --xml-output
+    | SourceDebName String
+    -- ^ The name of the Debian source package
+    deriving (Read, Show, Eq, Ord, Data, Typeable)
+
+retrieveMethodMD5 :: RetrieveMethod -> String
+retrieveMethodMD5 = show . md5 . L.pack . show
+
+-- | This type represents a package's fingerprint, (formerly its
+-- revision string,) which includes three pieces of information: how
+-- it was retrieved, the version number of the resulting Debian source
+-- package, and the names and version numbers of the build
+-- dependencies against which it was or is about to be built.
+data Fingerprint
+    = Fingerprint
+        { method :: RetrieveMethod
+          -- ^ The method which was used to retrieve the source code.
+        , retrievedAttributes :: Set RetrieveAttribute
+          -- ^ Identifying information about the result of the retrieve,
+          -- e.g. the debian version number, darcs repository tag, git
+          -- commit id.  Replaces upstreamVersion.
+        , upstreamVersion :: DebianVersion
+          -- ^ The version number in the changelog of the freshly downloaded
+          -- package, before any suffix is added by the autobuilder.
+        , buildDependencyVersions :: Set (PackageID BinPkgName)
+          -- ^ The names and version numbers of the build dependencies which
+          -- were present when the package was build.
+        }
+    deriving Show
+
+data DownstreamFingerprint
+    = DownstreamFingerprint
+        { upstreamFingerprint :: Fingerprint
+        , downstreamVersion :: DebianVersion
+          -- ^ This will be the the version field plus the suffix that
+          -- was added by the autobuilder.
+        }
+    deriving Show
+
+packageFingerprint :: SourcePackage -> Maybe DownstreamFingerprint
+packageFingerprint package =
+    maybe Nothing readDownstreamFingerprint (fmap (unpack . strip) . S.fieldValue "Fingerprint" . sourceParagraph $ package)
+    where
+      readDownstreamFingerprint :: String -> Maybe DownstreamFingerprint
+      readDownstreamFingerprint s =
+          maybe Nothing
+                (\ f -> Just $ DownstreamFingerprint { upstreamFingerprint = f
+                                                     , downstreamVersion = packageVersion . sourcePackageID $ package })
+                (readUpstreamFingerprint s)
+
+-- | Read a Fingerprint from the string written by showFingerprint
+-- into the .dsc file.
+readUpstreamFingerprint :: String -> Maybe Fingerprint
+readUpstreamFingerprint s =
+    case readMethod s of
+      Nothing -> Nothing
+      Just (m, etc) ->
+          -- See if there is a list of RetrieveAttribute - if not use the empty list
+          let (attrs, etc') = case reads etc :: [([RetrieveAttribute], String)] of
+                                [(x, etc'')] -> (x, etc'')
+                                _ -> ([], etc) in
+          case words etc' of
+            (sourceVersion : buildDeps)
+                | not (elem '=' sourceVersion) ->
+                    Just $ Fingerprint { method = m
+                                       , upstreamVersion = parseDebianVersion sourceVersion
+                                       , retrievedAttributes = Set.fromList attrs
+                                       , buildDependencyVersions = fromList (List.map readSimpleRelation buildDeps) }
+            -- Old style fingerprint field - no upstream
+            -- version number after the method.  I think
+            -- at this point we can ignore these.
+            _ -> Nothing
+
+readMethod :: String -> Maybe (RetrieveMethod, String)
+readMethod s =
+    -- New style: read the method directly from the beginning of s
+    case reads s :: [(RetrieveMethod, String)] of
+      [(m, etc)] -> Just (fix m, etc)
+      -- Old (broken) style: read a string, then read the method out
+      -- of it
+      _ -> case reads s :: [(String, String)] of
+             [(m, etc)] -> case maybeRead m of
+                             Nothing -> Nothing
+                             Just m' -> Just (fix m', etc)
+             _ -> Nothing
+    where
+      fix (Debianize x) = Debianize'' x Nothing
+      fix (Debianize' x _) = Debianize'' x Nothing
+      fix x = x
+
+{-
+modernizeMethod :: RetrieveMethod -> RetrieveMethod
+modernizeMethod = everywhere (mkT modernizeMethod1)
+
+modernizeMethod1 :: RetrieveMethod -> RetrieveMethod
+modernizeMethod1 (Debianize p) = Debianize' p []
+modernizeMethod1 x = x
+-}
+
+-- | The value that goes into the Fingerprint: field in the .dsc file.
+showFingerprint :: Fingerprint -> String
+showFingerprint (Fingerprint {method = m, upstreamVersion = sourceVersion, retrievedAttributes = attrs, buildDependencyVersions = versions}) =
+    intercalate " " ["(" ++ show m ++ ")",
+                     "[" ++ intercalate ", " (List.map show (toAscList attrs)) ++ "]",
+                     show (prettyDebianVersion sourceVersion),
+                     intercalate " " (List.map showSimpleRelation (toAscList versions))]
+
+showDependencies :: Fingerprint -> [String]
+showDependencies (Fingerprint {buildDependencyVersions = deps}) = toAscList $ Set.map showSimpleRelation deps
+
+-- | Show the dependency list without the version numbers.
+showDependencies' :: Fingerprint -> [String]
+showDependencies' (Fingerprint {buildDependencyVersions = deps}) = toAscList $ Set.map (ppDisplay . packageName) deps
+
+dependencyChanges :: Maybe DownstreamFingerprint -> Fingerprint -> String
+dependencyChanges old new =
+    depChanges changedDeps
+    where
+      depChanges [] = ""
+      depChanges _ = "  * Build dependency changes:" ++ prefix ++ intercalate prefix padded ++ "\n"
+      padded = List.map concat . columns . List.map showDepChange $ changedDeps
+      changedDeps = Set.toList (Set.difference (buildDependencyVersions new) (buildDependencyVersions new))
+      showDepChange newDep =
+          case toList (Set.filter (hasName (packageName newDep)) (maybe empty (buildDependencyVersions . upstreamFingerprint) old)) of
+            [] -> [" " ++ unBinPkgName (packageName newDep) ++ ": ", "(none)", " -> ", show (prettyDebianVersion (packageVersion newDep))]
+            (oldDep : _) -> [" " ++ unBinPkgName (packageName newDep) ++ ": ", show (prettyDebianVersion (packageVersion oldDep)), " -> ", show (prettyDebianVersion (packageVersion newDep))]
+      hasName name = ((== name) . packageName)
+      prefix = "\n    "
diff --git a/testdata/debian/Debian/Repo/Internal/Apt.hs b/testdata/debian/Debian/Repo/Internal/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Internal/Apt.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings,
+             PackageImports, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Internal.Apt
+    ( AptImage(..)
+    , MonadApt(getApt, putApt)
+    , modifyApt
+    , cacheRootDir
+    , createAptImage
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Category ((.))
+import Control.Monad.State (MonadState(get, put), StateT)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Debian.Arch (Arch)
+import Debian.Pretty (ppDisplay)
+import Debian.Release (ReleaseName(relName))
+import Debian.Repo.EnvPath (EnvRoot(EnvRoot, rootPath))
+import Debian.Repo.Internal.IO (buildArchOfRoot)
+import Debian.Repo.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Prelude (replaceFile, writeFileIfMissing)
+import Debian.Repo.Slice (NamedSliceList(sliceList, sliceListName))
+import Debian.Repo.Top (dists, MonadTop)
+import Prelude hiding ((.))
+import System.Directory (createDirectoryIfMissing)
+import System.FilePath ((</>))
+
+data AptImage =
+    AptImage { aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSources :: NamedSliceList
+             , aptSourcePackageCache :: Maybe [SourcePackage]
+             , aptBinaryPackageCache :: Maybe [BinaryPackage]
+             }
+
+class (Monad m, Functor m) => MonadApt m where
+    getApt :: m AptImage
+    putApt :: AptImage -> m ()
+
+modifyApt :: MonadApt m => (AptImage -> AptImage) -> m ()
+modifyApt f = getApt >>= putApt . f
+
+instance (Monad m, Functor m) => MonadApt (StateT AptImage m) where
+    getApt = get
+    putApt = put
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (sliceListName (aptImageSources apt))
+
+instance Ord AptImage where
+    compare a b = compare (sliceListName . aptImageSources $ a) (sliceListName . aptImageSources $ b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
+
+createAptImage :: (MonadTop m, MonadIO m) => NamedSliceList -> m AptImage
+createAptImage sources = do
+  root <- cacheRootDir (sliceListName sources)
+  liftIO $ do
+    arch <- buildArchOfRoot
+    let apt = AptImage { aptImageRoot = root
+                       , aptImageArch = arch
+                       , aptImageSources = sources
+                       , aptSourcePackageCache = Nothing
+                       , aptBinaryPackageCache = Nothing }
+
+    --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+    createDirectoryIfMissing True (rootPath root ++ "/var/lib/apt/lists/partial")
+    createDirectoryIfMissing True (rootPath root ++ "/var/lib/apt/lists/partial")
+    createDirectoryIfMissing True (rootPath root ++ "/var/cache/apt/archives/partial")
+    createDirectoryIfMissing True (rootPath root ++ "/var/lib/dpkg")
+    createDirectoryIfMissing True (rootPath root ++ "/etc/apt")
+    writeFileIfMissing True (rootPath root ++ "/var/lib/dpkg/status") ""
+    writeFileIfMissing True (rootPath root ++ "/var/lib/dpkg/diversions") ""
+    -- We need to create the local pool before updating so the
+    -- sources.list will be valid.
+    let sourceListText = ppDisplay (sliceList sources)
+    -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+    replaceFile (rootPath root ++ "/etc/apt/sources.list") sourceListText
+    return apt
+
+cacheRootDir :: MonadTop m => ReleaseName -> m EnvRoot
+cacheRootDir release = EnvRoot . (</> relName release </> "aptEnv") <$> dists
diff --git a/testdata/debian/Debian/Repo/Internal/IO.hs b/testdata/debian/Debian/Repo/Internal/IO.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Internal/IO.hs
@@ -0,0 +1,26 @@
+-- | Ensure that any OSKey or AptKey value available outside this
+-- module corresponds to an existing entry in the corresponding
+-- Map in ReposState.
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Internal.IO
+    ( buildArchOfRoot
+    ) where
+
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..))
+import System.Exit (ExitCode(ExitSuccess))
+import System.Process (readProcessWithExitCode)
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
diff --git a/testdata/debian/Debian/Repo/Internal/Repos.hs b/testdata/debian/Debian/Repo/Internal/Repos.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Internal/Repos.hs
@@ -0,0 +1,215 @@
+-- | Manage state information about the available repositories,
+-- releases, OS images, and Apt images.
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables, TemplateHaskell #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Debian.Repo.Internal.Repos
+    ( MonadRepos(getRepos, putRepos)
+    , ReposState
+    , runReposT
+
+    , putOSImage
+    , osFromRoot
+
+    , AptKey
+    , getApt
+    , getAptKey
+    , putAptImage
+    , evalMonadApt
+
+    , repoByURI
+    , putRepo
+
+    , ReleaseKey
+    , findRelease
+    , putRelease
+    , releaseByKey
+
+    , MonadReposCached
+    , runReposCachedT
+
+    , syncOS
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Applicative.Error (maybeRead)
+import Control.Exception (SomeException)
+import Control.Monad (unless)
+import Control.Monad.Catch (bracket, catch, MonadCatch, MonadMask)
+import Control.Monad.State (MonadIO(..), MonadState(get, put), StateT(runStateT))
+import Control.Monad.Trans (lift)
+import Data.Map as Map (empty, fromList, insert, lookup, Map, toList, union)
+import Data.Maybe (fromMaybe)
+import Debian.Release (ReleaseName)
+import Debian.Repo.EnvPath (EnvRoot)
+import Debian.Repo.Internal.Apt (AptImage(aptImageRoot))
+import Debian.Repo.OSImage (OSImage(osRoot), syncOS')
+import Debian.Repo.Prelude.Verbosity (qPutStrLn)
+import Debian.Repo.Release (Release(releaseName))
+import Debian.Repo.RemoteRepository (RemoteRepository)
+import Debian.Repo.Repo (Repo, repoKey, RepoKey(..))
+import Debian.Repo.Top (MonadTop, runTopT, sub, TopT)
+import Debian.URI (URI')
+import System.IO.Error (isDoesNotExistError)
+import qualified System.Posix.Files as F (removeLink)
+
+data ReleaseKey = ReleaseKey RepoKey ReleaseName deriving (Eq, Ord, Show)
+
+newtype AptKey = AptKey EnvRoot deriving (Eq, Ord, Show)
+
+-- | This represents the state of the IO system.
+data ReposState
+    = ReposState
+      { repoMap :: Map.Map URI' RemoteRepository		-- ^ Map to look up known (remote) Repository objects
+      , releaseMap :: Map.Map ReleaseKey Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map AptKey AptImage	-- ^ Map to look up prepared AptImage objects
+      , osImageMap :: Map.Map EnvRoot OSImage	-- ^ Map to look up prepared OSImage objects
+      }
+
+runReposT :: Monad m => StateT ReposState m a -> m a
+runReposT action = (runStateT action) initState >>= return . fst
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: ReposState
+initState = ReposState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , osImageMap = Map.empty
+            }
+
+-- | A monad to support the IO requirements of the autobuilder.
+class (MonadCatch m, MonadIO m, Functor m) => MonadRepos m where
+    getRepos :: m ReposState
+    putRepos :: ReposState -> m ()
+
+instance (MonadCatch m, MonadIO m, Functor m) => MonadRepos (StateT ReposState m) where
+    getRepos = get
+    putRepos = put
+
+{-
+instance (MonadRepos m, Functor m) => MonadRepos (StateT s m) where
+    getRepos = lift getRepos
+    putRepos = lift . putRepos
+-}
+
+instance MonadRepos m => MonadRepos (StateT EnvRoot m) where
+    getRepos = lift getRepos
+    putRepos = lift . putRepos
+
+modifyRepos :: MonadRepos m => (ReposState -> ReposState) -> m ()
+modifyRepos f = getRepos >>= putRepos . f
+
+-- | Like @MonadRepos@, but is also an instance of MonadTop and tries to
+-- load and save a list of cached repositories from @top/repoCache@.
+class (MonadRepos m, MonadTop m, MonadCatch m) => MonadReposCached m
+
+type ReposCachedT m = TopT (StateT ReposState m)
+
+-- | To run a DebT we bracket an action with commands to load and save
+-- the repository list.
+runReposCachedT :: (MonadIO m, MonadCatch m, Functor m, MonadMask m) => FilePath -> ReposCachedT m a -> m a
+runReposCachedT top action = do
+  qPutStrLn "Running MonadReposCached..."
+  r <- runReposT $ runTopT top $ bracket loadRepoCache (\ r -> saveRepoCache >> return r) (\ () -> action)
+  qPutStrLn "Exited MonadReposCached..."
+  return r
+
+instance (MonadCatch m, MonadIO m, Functor m) => MonadReposCached (ReposCachedT m)
+
+instance (MonadCatch m, MonadIO m, Functor m) => MonadRepos (ReposCachedT m) where
+    getRepos = lift get
+    putRepos = lift . put
+
+putOSImage :: MonadRepos m => OSImage -> m ()
+putOSImage repo =
+    modifyRepos (\ s -> s {osImageMap = Map.insert (osRoot repo) repo (osImageMap s)})
+
+osFromRoot :: MonadRepos m => EnvRoot -> m (Maybe OSImage)
+osFromRoot root = Map.lookup root . osImageMap <$> getRepos
+
+putRepo :: MonadRepos m => URI' -> RemoteRepository -> m ()
+putRepo uri repo = modifyRepos (\ s -> s {repoMap = Map.insert uri repo (repoMap s)})
+
+repoByURI :: MonadRepos m => URI' -> m (Maybe RemoteRepository)
+repoByURI uri = Map.lookup uri . repoMap <$> getRepos
+
+getApt :: MonadRepos m => EnvRoot -> m (Maybe AptImage)
+getApt root = (Map.lookup (AptKey root) . aptImageMap) <$> getRepos
+
+getAptKey :: MonadRepos m => EnvRoot -> m (Maybe AptKey)
+getAptKey root = fmap (AptKey . aptImageRoot) <$> (getApt root)
+
+findRelease :: (Repo r, MonadRepos m) => r -> ReleaseName -> m (Maybe Release)
+findRelease repo dist = (Map.lookup (ReleaseKey (repoKey repo) dist) . releaseMap) <$> getRepos
+
+releaseByKey :: MonadRepos m => ReleaseKey -> m Release
+releaseByKey key = do
+  Just rel <- (Map.lookup key . releaseMap) <$> getRepos
+  return rel
+
+putRelease :: (Repo r, MonadRepos m) => r -> Release -> m ReleaseKey
+putRelease repo release = do
+    let key = ReleaseKey (repoKey repo) (releaseName release)
+    modifyRepos (\ s -> s {releaseMap = Map.insert key release (releaseMap s)})
+    return key
+
+putAptImage :: MonadRepos m => AptImage -> m AptKey
+putAptImage repo = do
+  let key = AptKey (aptImageRoot repo)
+  modifyRepos (\ s -> s {aptImageMap = Map.insert key repo (aptImageMap s)})
+  return key
+
+-- | Run MonadOS and update the osImageMap with the modified value
+evalMonadApt :: MonadRepos m => StateT AptImage m a -> AptKey -> m a
+evalMonadApt task (AptKey key) = do
+  Just apt <- getApt key
+  (a, apt') <- runStateT task apt
+  putAptImage apt'
+  return a
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: MonadReposCached m => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       mp <- liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty))
+       modifyRepos (\ s -> s {repoMap = mp})
+    where
+      loadRepoCache' :: FilePath -> IO (Map URI' RemoteRepository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: MonadReposCached m => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- repoMap <$> getRepos
+             repoCache <- liftIO $ loadCache path
+             let merged = Map.union live repoCache
+             liftIO (F.removeLink path `catch` (\e -> unless (isDoesNotExistError e) (ioError e)) >>
+                     writeFile path (show . Map.toList $ merged))
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map URI' RemoteRepository)
+            loadCache path =
+                readFile path `catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+syncOS :: (MonadTop m, MonadRepos m) => OSImage -> EnvRoot -> m OSImage
+syncOS srcOS dstRoot = do
+  dstOS <- liftIO $ syncOS' srcOS dstRoot
+  putOSImage dstOS
+  dstOS' <- osFromRoot dstRoot
+  maybe (error ("syncOS failed for " ++ show dstRoot)) return dstOS'
diff --git a/testdata/debian/Debian/Repo/LocalRepository.hs b/testdata/debian/Debian/Repo/LocalRepository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/LocalRepository.hs
@@ -0,0 +1,356 @@
+-- | A repository located on localhost
+{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, StandaloneDeriving, ScopedTypeVariables, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.LocalRepository
+    ( LocalRepository(..)
+    , Layout(..)
+    , poolDir'
+    , readLocalRepo
+    , copyLocalRepo -- repoCD
+    , setRepositoryCompatibility
+    , verifyUploadURI
+    , uploadRemote
+    , uploadLocal
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Monad.Trans (liftIO, MonadIO)
+import qualified Data.ByteString.Lazy as L (ByteString, empty)
+import Data.List (groupBy, isPrefixOf, partition, sort, sortBy)
+import Data.Maybe (catMaybes, listToMaybe)
+import qualified Data.Set as Set (fromList, member)
+import Data.Text as T (unpack)
+import Data.Time (NominalDiffTime)
+import Debian.Arch (Arch, parseArch)
+import Debian.Changes (ChangedFileSpec(changedFileName, changedFileSection), ChangesFile(changeDir, changeFiles, changeInfo, changePackage, changeRelease, changeVersion))
+import qualified Debian.Control.Text as S (Control'(Control), ControlFunctions(parseControlFromFile), fieldValue)
+import qualified Debian.Control.Text as T (fieldValue)
+import Debian.Pretty (PP(..), ppDisplay)
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section(..), sectionName', SubSection(section))
+import Debian.Repo.Changes (changeKey, changePath, findChangesFiles)
+import Debian.Repo.EnvPath (EnvPath(envPath), outsidePath)
+import Debian.Repo.Fingerprint (readUpstreamFingerprint)
+import Debian.Repo.Prelude (cond, maybeWriteFile, partitionM, replaceFile, rsync)
+import Debian.Repo.Prelude.SSH (sshVerify)
+import Debian.Repo.Prelude.Verbosity (qPutStrLn, timeTask, readProcFailing)
+import Debian.Repo.Release (parseReleaseFile, Release)
+import Debian.Repo.Repo (compatibilityFile, libraryCompatibilityLevel, Repo(..), RepoKey(..))
+import Debian.URI (URI(uriAuthority, uriPath), URIAuth(uriPort, uriRegName, uriUserInfo), uriToString')
+import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)
+import Network.URI (URI(..))
+import System.Directory (createDirectoryIfMissing, doesFileExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitFailure), ExitCode(ExitSuccess))
+import System.FilePath ((</>), splitFileName)
+import qualified System.Posix.Files as F (createLink, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink)
+import System.Process (readProcessWithExitCode, CreateProcess(cwd, cmdspec), showCommandForUser, proc)
+import System.Process.Chunks (Chunk(..), collectProcessResult, showCmdSpecForUser)
+import Text.Regex (matchRegex, mkRegex)
+import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), text)
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot :: EnvPath
+      , repoLayout :: (Maybe Layout)
+      , repoReleaseInfoLocal :: [Release]
+      } deriving (Read, Show, Ord, Eq)
+
+instance Pretty (PP LocalRepository) where
+    pPrint (PP (LocalRepository root _ _)) =
+        text $ show $ URI { uriScheme = "file:"
+                          , uriAuthority = Nothing
+                          , uriPath = envPath root
+                          , uriQuery = ""
+                          , uriFragment = "" }
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show, Bounded, Enum)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section' source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section' </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+readLocalRepo :: MonadIO m => EnvPath -> Maybe Layout -> m (Maybe LocalRepository)
+readLocalRepo root layout =
+    do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+       (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+       linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+       let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+       let distGroups = groupBy fstEq . sort $ aliasPairs
+       let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+       releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+       case releaseInfo of
+         [] -> return Nothing
+         _ -> return $ Just $ LocalRepository { repoRoot = root
+                                              , repoLayout = layout
+                                              , repoReleaseInfoLocal = releaseInfo }
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist </> "Release"
+      distDir = outsidePath root </> "dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadIO m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       (result :: (ExitCode, String, String)) <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         (ExitSuccess, _, _) -> return $ repo {repoRoot = dest}
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path (show libraryCompatibilityLevel ++ "\n")
+    where path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- |The file produced by dupload when a package upload attempt is made.
+data UploadFile = Upload FilePath String DebianVersion Arch
+
+-- |Make sure we can access the upload uri without typing a password.
+verifyUploadURI :: MonadIO m => Bool -> URI -> m ()
+verifyUploadURI doExport uri = do
+  qPutStrLn ("Verifying upload URI: " ++ show uri)
+  case doExport of
+    True -> export
+    False -> verify >> mkdir
+    where
+      export =
+          do -- The code in sshExport needs to be rewritten.
+             -- liftIO $ uncurry sshExport (uriDest uri) >>= either fail return
+             verify
+             mkdir
+      verify =
+          do result <- liftIO $ uncurry sshVerify (uriDest uri)
+             case result of
+               Right () -> return ()
+               Left s -> error $ "Unable to reach " ++ uriToString' uri ++ ": " ++ s
+             mkdir
+      mkdir =
+          case uriAuthority uri of
+            Nothing -> error $ "Internal error 7"
+            Just auth ->
+                do let cmd = "ssh"
+                       args = [uriUserInfo auth ++ uriRegName auth ++ uriPort auth,
+                               "mkdir", "-p", uriPath uri ++ "/incoming"]
+                   (result, _, _) <- liftIO (readProcessWithExitCode cmd args "")
+                   case result of
+                     ExitSuccess -> return ()
+                     _ -> fail $ showCommandForUser cmd args ++ " -> " ++ show result
+
+uriDest :: URI -> ([Char], Maybe Int)
+uriDest uri =
+    (uriUserInfo auth ++ uriRegName auth, port)
+    where
+      auth = maybe (error "Internal error 8") id (uriAuthority uri)
+      port =
+          case uriPort auth of
+            (':' : number) -> Just (read number)
+            "" -> Nothing
+            x -> error $ "Internal error 9: invalid port " ++ x
+
+-- | Upload all the packages in a local repository to a the incoming
+-- directory of a remote repository (using dupload.)
+uploadRemote :: LocalRepository		-- ^ Local repository holding the packages.
+             -> URI			-- ^ URI of upload repository
+             -> IO [Failing ([Chunk L.ByteString], NominalDiffTime)]
+uploadRemote repo uri =
+    do let dir = (outsidePath root)
+       changesFiles <- findChangesFiles (outsidePath root)
+       let changesFileGroups = map (sortBy compareVersions) . groupByNameAndDist $ changesFiles
+       let newestChangesFiles = catMaybes (map listToMaybe changesFileGroups)
+       -- hPutStrLn stderr $ "Newest: " ++ show newestChangesFiles
+       uploaded <- (Set.fromList .
+                    map (\ [_, name', version, arch] -> (name', parseDebianVersion version, parseArch arch)) .
+                    catMaybes .
+                    map (matchRegex (mkRegex "^(.*/)?([^_]*)_(.*)_([^.]*)\\.upload$"))) <$> getDirectoryContents dir
+       let (readyChangesFiles, uploadedChangesFiles) = partition (\ f -> not . Set.member (changeKey f) $ uploaded) newestChangesFiles
+       -- hPutStrLn stderr $ "Uploaded: " ++ show uploadedChangesFiles
+       -- hPutStrLn stderr $ "Ready: " ++ show readyChangesFiles
+       validChangesFiles <- mapM validRevision' readyChangesFiles
+       -- hPutStrLn stderr $ "Valid: " ++ show validChangesFiles
+       mapM dupload' validChangesFiles
+    where
+      keepNewest (Success newest : older) =
+          Success newest : map tooOld older
+      keepNewest xs = xs
+      -- Add to Control.Applicative.Error
+      -- partitionFailing :: [Failing a] -> ([[String]], [a])
+      -- partitionFailing = foldr f ([], []) where f (Failure ms) (msgs, xs) = (ms : msgs, xs)
+      --                                           f (Success x) (msgs, xs) = (msgs, x : xs)
+      tooOld (Failure x) = Failure x
+      tooOld (Success x) = Failure ["Not the newest version in incoming: " ++ ppDisplay x]
+      successes (Success x : xs) = x : successes xs
+      successes (Failure _ : xs) = successes xs
+      successes [] = []
+      root = repoRoot repo
+{-
+      rejectOlder :: ([ChangesFile], [(ChangesFile, String)]) ->  ([ChangesFile], [(ChangesFile, String)])
+      rejectOlder (accept, reject) =
+          (accept', (map tag reject' ++ reject))
+          where accept' = map head sortedGroups
+                reject' = concat . map tail $ sortedGroups
+                sortedGroups = map (sortBy compareVersions) (groupByNameAndDist accept)
+                tag x = (x, "Not the newest version in incoming")
+-}
+      compareVersions a b = compare (changeVersion b) (changeVersion a)
+      groupByNameAndDist :: [ChangesFile] -> [[ChangesFile]]
+      groupByNameAndDist = groupBy equalNameAndDist . sortBy compareNameAndDist
+      equalNameAndDist a b = compareNameAndDist a b == EQ
+      compareNameAndDist a b =
+          case compare (changePackage a) (changePackage b) of
+            EQ -> compare (changeRelease a) (changeRelease b)
+            x -> x
+      --showReject (changes, tag) = Debian.Repo.Changes.name changes ++ ": " ++ tag
+      dupload' (Failure x) = return (Failure x)
+      dupload' (Success c) = liftIO (dupload uri (outsidePath root) (changePath c))
+
+validRevision' :: ChangesFile -> IO (Failing ChangesFile)
+validRevision' c = validRevision
+    where
+      validRevision :: IO (Failing ChangesFile)
+      validRevision =
+          doesFileExist dscPath >>=
+                        cond (S.parseControlFromFile dscPath >>=
+                              either (\ e -> return (Failure [show e])) (checkRevision dscPath))
+                             (return (Success c))
+      dscPath = changeDir c </> changePackage c ++ "_" ++ show (prettyDebianVersion (changeVersion c)) ++ ".dsc"
+      checkRevision :: FilePath -> S.Control' String -> IO (Failing ChangesFile)
+      checkRevision dscPath' (S.Control [p]) =
+          case maybe (Failure ["Missing Fingerprint field in " ++ dscPath'])
+                     (\ s -> maybe (Failure ["Parse error in revision string: " ++  show s]) Success (readUpstreamFingerprint s))
+                     (S.fieldValue "Fingerprint" p) of
+            Failure msgs -> return (Failure msgs)
+            -- Success x | x == invalidRevision -> return (Failure ["Invalid revision: " ++ show x])
+            Success _ -> return (Success c)
+      checkRevision dscPath' _ = return (Failure ["Invalid .dsc file: " ++ show dscPath'])
+      -- invalidRevision = "none"
+      -- Parse the "Fingerprint:" value describing the origin of the
+      -- package's source and the dependency versions used to build it:
+      --   Revision: <revisionstring> dep1=ver1 dep2=ver2 ...
+      -- parseRevision :: String -> Failing (String, [PackageID BinPkgName])
+      -- parseRevision s =
+      --     case reads s :: [(String, String)] of
+      --       [(method, etc)] ->
+      --           case words etc of
+      --             (sourceVersion : buildDeps)
+      --               | not (elem '=' sourceVersion) ->
+      --                   Success (method, map readSimpleRelation buildDeps)
+      --             buildDeps ->
+      --                   Success (method, map readSimpleRelation buildDeps)
+      --       _ -> Failure ["Invalid revision field: " ++ s]
+
+uploadKey :: UploadFile -> (String, DebianVersion, Arch)
+uploadKey (Upload _ name ver arch) = (name, ver, arch)
+
+-- |Parse the name of a .upload file
+parseUploadFilename :: FilePath
+                    -> String
+                    -> Failing UploadFile
+parseUploadFilename dir name =
+    case matchRegex (mkRegex "^(.*/)?([^_]*)_(.*)_([^.]*)\\.upload$") name of
+      Just [_, name', version, arch] -> Success (Upload dir name' (parseDebianVersion version) (parseArch arch))
+      _ -> Failure ["Invalid .upload file name: " ++ name]
+
+{-
+accept :: (a -> Bool) -> (a -> (a, String)) -> ([a], [(a, String)]) -> ([a], [(a, String)])
+accept p tag (accepted, rejected) =
+    (accepted', map tag rejected' ++ rejected)
+    where (accepted', rejected') = partition p accepted
+
+acceptM :: (Monad m) => (a -> m Bool) -> (a -> (a, String)) -> ([a], [(a, String)]) -> m ([a], [(a, String)])
+acceptM p tag (accept, reject) =
+    do (accept', reject') <- partitionM p accept
+       return (accept', (map tag reject' ++ reject))
+-}
+
+-- |Run dupload on a changes file with an optional host (--to)
+-- argument.
+dupload :: URI		-- user
+        -> FilePath	-- The directory containing the .changes file
+        -> String	-- The name of the .changes file to upload
+        -> IO (Failing ([Chunk L.ByteString], NominalDiffTime))
+dupload uri dir changesFile  =
+    case uriAuthority uri of
+      Nothing -> error ("Invalid Upload-URI: " ++ uriToString' uri)
+      Just auth -> do
+        let config = ("package config;\n" ++
+                      "$cfg{'default'} = {\n" ++
+                      "        fqdn => \"" ++ uriRegName auth ++ uriPort auth ++ "\",\n" ++
+                      "        method => \"scpb\",\n" ++
+	              "        login => \"" ++ init (uriUserInfo auth) ++ "\",\n" ++
+                      "        incoming => \"" ++ uriPath uri ++ "/incoming\",\n" ++
+                      "        dinstall_runs => 1,\n" ++
+                      "};\n\n" ++
+		      "$preupload{'changes'} = '';\n\n" ++
+                      "1;\n")
+        replaceFile (dir ++ "/dupload.conf") config
+        let cmd = (proc "dupload" ["--to", "default", "-c", changesFile]) {cwd = Just dir}
+        qPutStrLn ("Uploading " ++ show changesFile)
+        (chunks, elapsed) <- timeTask $ readProcFailing cmd L.empty
+        qPutStrLn ("Upload finished, elapsed time " ++ show elapsed)
+        let (code, out) = collectProcessResult chunks
+        case code of
+          ExitFailure _ ->
+              do let message = "dupload in " ++ dir ++ " failed: " ++ showCmdSpecForUser (cmdspec cmd) ++ " -> " ++ show code
+                 qPutStrLn message
+                 return $ Failure [message]
+          ExitSuccess -> return $ Success (out, elapsed)
+
+ignore :: forall a. IO (Either String [Chunk L.ByteString]) -> a -> IO (Either String [Chunk L.ByteString])
+ignore result _ = result
+
+-- | Move a build result into a local repository's 'incoming' directory.
+uploadLocal :: LocalRepository -> ChangesFile -> IO ()
+uploadLocal repo changesFile =
+    do let paths = map (\ file -> changeDir changesFile </> changedFileName file) (changeFiles changesFile)
+       mapM_ (liftIO . install (outsidePath root)) (changePath changesFile : paths)
+    where
+      root = repoRoot repo
+      -- Hard link a file into the incoming directory
+      install root' path =
+	  do removeIfExists (dest root' path)
+	     F.createLink path (dest root' path)
+             -- F.removeLink path
+      dest root' path = root' ++ "/incoming/" ++ snd (splitFileName path)
+      removeIfExists path =
+	  do exists <- doesFileExist path
+	     if exists then F.removeLink path  else return ()
diff --git a/testdata/debian/Debian/Repo/MonadOS.hs b/testdata/debian/Debian/Repo/MonadOS.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/MonadOS.hs
@@ -0,0 +1,180 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.MonadOS
+    ( MonadOS(getOS, putOS, modifyOS)
+    , evalMonadOS
+    , updateLists
+    , withProc
+    , withTmp
+    , aptGetInstall
+    , syncLocalPool
+    , osFlushPackageCache
+    , buildEssential
+    , Debian.Repo.MonadOS.syncOS
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.DeepSeq (force)
+import Control.Exception (evaluate, SomeException, catch)
+import Control.Monad.Catch (bracket, MonadCatch, MonadMask, throwM, try)
+import Control.Monad.State (MonadState(get), StateT, evalStateT, get)
+import Control.Monad.Trans (liftIO, MonadIO, lift)
+import Data.ByteString.Lazy as L (empty)
+import Data.Time (NominalDiffTime)
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (PkgName, Relations)
+import Debian.Repo.EnvPath (EnvPath(EnvPath, envPath, envRoot), EnvRoot(rootPath))
+import Debian.Repo.Internal.Repos (MonadRepos, osFromRoot, putOSImage, syncOS)
+import Debian.Repo.LocalRepository (copyLocalRepo)
+import Debian.Repo.OSImage as OS (OSImage(osRoot, osLocalMaster, osLocalCopy, osSourcePackageCache, osBinaryPackageCache))
+import qualified Debian.Repo.OSImage as OS (buildEssential)
+import Debian.Repo.Prelude.Verbosity (quieter, timeTask, readProcFailing, ePutStrLn)
+import Debian.Repo.Top (MonadTop)
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Directory (createDirectoryIfMissing)
+import System.Exit (ExitCode(ExitFailure))
+import System.FilePath ((</>))
+import System.Process (proc)
+import System.Process.Chunks (collectProcessTriple)
+import System.Unix.Chroot (useEnv)
+
+-- | The problem with having an OSImage in the state of MonadOS is
+-- that then we are modifying a copy of the OSImage in MonadRepos, we
+-- want to go into MonadRepos and modify the map element there.  So
+-- instead put an EnvRoot to look up the OSImage.
+class (Monad m, Functor m) => MonadOS m where
+    getOS :: m OSImage
+    putOS :: OSImage -> m ()
+    modifyOS :: (OSImage -> OSImage) -> m ()
+
+instance MonadRepos m => MonadOS (StateT EnvRoot m) where
+    getOS = get >>= \ root -> maybe (error "getOS") id <$> (lift $ osFromRoot root)
+    putOS = lift . putOSImage
+    modifyOS f = getOS >>= putOS . f
+
+-- | Run MonadOS and update the osImageMap with the modified value
+evalMonadOS :: MonadRepos m => StateT EnvRoot m a -> EnvRoot -> m a
+evalMonadOS task root = do
+  a <- evalStateT task root
+  return a
+
+-- | Run @apt-get update@ and @apt-get dist-upgrade@.  If @update@
+-- fails, run @dpkg --configure -a@ before running @dist-upgrade@.
+updateLists :: (MonadOS m, MonadIO m, MonadCatch m, MonadMask m) => m NominalDiffTime
+updateLists = quieter 1 $
+    do root <-rootPath . osRoot <$> getOS
+       withProc $ liftIO $ do
+         (code, _, _) <- useEnv root forceList (readProcFailing update "") >>= return . collectProcessTriple
+         _ <- case code of
+                ExitFailure _ ->
+                    do _ <- useEnv root forceList (readProcFailing configure "")
+                       useEnv root forceList (readProcFailing update "")
+                _ -> return []
+         (_, elapsed) <- timeTask (useEnv root forceList (readProcFailing upgrade ""))
+         return elapsed
+    where
+       update = proc "apt-get" ["update"]
+       configure = proc "dpkg" ["--configure", "-a"]
+       upgrade = proc "apt-get" ["-y", "--force-yes", "dist-upgrade"]
+
+-- | Do an IO task in the build environment with /proc mounted.
+withProc :: forall m c. (MonadOS m, MonadIO m, MonadCatch m, MonadMask m) => m c -> m c
+withProc task =
+    do root <- rootPath . osRoot <$> getOS
+       let proc' = root </> "proc"
+           sys = root </> "sys"
+           pre :: m ()
+           mountProc = proc "mount" ["--bind", "/proc", proc']
+           mountSys = proc "mount" ["--bind", "/sys", sys]
+           umountProc = proc "umount" [proc']
+           umountSys = proc "umount" [sys]
+           umountProcLazy = proc "umount" ["-l", proc']
+           umountSysLazy = proc "umount" ["-l", sys]
+
+           pre = liftIO (do createDirectoryIfMissing True proc'
+                            readProcFailing mountProc L.empty
+                            createDirectoryIfMissing True sys
+                            readProcFailing mountSys L.empty
+                            return ())
+           post :: () -> m ()
+           post _ = liftIO $ do readProcFailing umountProc L.empty
+                                  `catch` (\ (e :: IOError) ->
+                                               ePutStrLn ("Exception unmounting proc, trying lazy: " ++ show e) >>
+                                               readProcFailing umountProcLazy L.empty)
+                                readProcFailing umountSys L.empty
+                                  `catch` (\ (e :: IOError) ->
+                                               ePutStrLn ("Exception unmounting sys, trying lazy: " ++ show e) >>
+                                               readProcFailing umountSysLazy L.empty)
+                                return ()
+           task' :: () -> m c
+           task' _ = task
+       bracket pre post task'
+
+-- | Do an IO task in the build environment with /proc mounted.
+withTmp :: forall m c. (MonadOS m, MonadIO m, MonadCatch m, MonadMask m) => m c -> m c
+withTmp task =
+    do root <- rootPath . osRoot <$> getOS
+       let dir = root </> "tmp"
+           mountTmp = proc "mount" ["--bind", "/tmp", dir]
+           umountTmp = proc "umount" [dir]
+           pre :: m ()
+           pre = liftIO $ do createDirectoryIfMissing True dir
+                             readProcFailing mountTmp L.empty
+                             return ()
+           post :: () -> m ()
+           post _ = liftIO $ readProcFailing umountTmp L.empty >> return ()
+           task' :: () -> m c
+           task' _ = try task >>= either (\ (e :: SomeException) -> throwM e) return
+       bracket pre post task'
+
+-- | Run an apt-get command in a particular directory with a
+-- particular list of packages.  Note that apt-get source works for
+-- binary or source package names.
+aptGetInstall :: (MonadOS m, MonadIO m, PkgName n) => [(n, Maybe DebianVersion)] -> m ()
+aptGetInstall packages =
+    do root <- rootPath . osRoot <$> getOS
+       liftIO $ useEnv root (return . force) $ do
+         readProcFailing p L.empty
+         return ()
+    where
+      p = proc "apt-get" args'
+      args' = ["-y", "--force-yes", "install"] ++ map formatPackage packages
+      formatPackage (name, Nothing) = ppDisplay name
+      formatPackage (name, Just version) = ppDisplay name ++ "=" ++ show (prettyDebianVersion version)
+
+-- | This is a deepseq thing
+forceList :: [a] -> IO [a]
+forceList output = evaluate (length output) >> return output
+
+-- | Use rsync to synchronize the pool of locally built packages from
+-- outside the build environment to the location inside the
+-- environment where apt can see and install the packages.  On the
+-- assumption that we are doing this because the pool changed, we also
+-- flush the cached package lists.
+syncLocalPool :: (MonadIO m, MonadOS m, MonadCatch m, MonadMask m) => m ()
+syncLocalPool =
+    do os <- getOS
+       repo' <- copyLocalRepo (EnvPath {envRoot = osRoot os, envPath = "/work/localpool"}) (osLocalMaster os)
+       putOS (os {osLocalCopy = repo'})
+       updateLists
+       -- Presumably we are doing this because the pool changed, and
+       -- that invalidates the OS package lists.
+       osFlushPackageCache
+
+osFlushPackageCache :: MonadOS m => m ()
+osFlushPackageCache = modifyOS (\ os -> os {osSourcePackageCache = Nothing, osBinaryPackageCache = Nothing})
+
+-- | Get the version of the newest ghc available in a build environment.
+-- ghcNewestAvailableVersion :: (MonadIO m, Functor m, MonadState OSImage m) => m (Maybe DebianVersion)
+-- ghcNewestAvailableVersion = do
+--   root <- rootPath . osRoot <$> get
+--   liftIO $ GHC.ghcNewestAvailableVersion root
+
+buildEssential :: (MonadOS m, MonadIO m) => m Relations
+buildEssential = getOS >>= liftIO . OS.buildEssential
+
+syncOS :: (MonadOS m, MonadTop m, MonadRepos m) => EnvRoot -> m ()
+syncOS dstRoot =
+    do srcOS <- getOS
+       dstOS <- Debian.Repo.Internal.Repos.syncOS srcOS dstRoot
+       putOS dstOS
diff --git a/testdata/debian/Debian/Repo/OSImage.hs b/testdata/debian/Debian/Repo/OSImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/OSImage.hs
@@ -0,0 +1,427 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.OSImage
+    (
+    -- * OSImage type
+      OSImage(osRoot, osBaseDistro, osArch, osLocalMaster, osLocalCopy, osSourcePackageCache, osBinaryPackageCache)
+    , createOSImage
+    , cloneOSImage
+
+    -- * OSImage Creation
+    , pbuilder
+    , debootstrap
+    , syncOS'
+
+    -- * OSImage Queries
+    , osFullDistro
+    , buildEssential
+
+    -- * OSImage Manipulation
+    , neuterEnv
+    , restoreEnv
+    , localeGen
+    , removeEnv
+    ) where
+
+import Control.Exception (evaluate, SomeException)
+import Control.Monad.Catch (try)
+import Control.Monad.State (MonadIO(..))
+import Data.Data (Data)
+import Data.List (intercalate)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch)
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (ParseRelations(parseRelations), Relations)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName(relName))
+import Debian.Repo.EnvPath (EnvPath(EnvPath, envPath, envRoot), EnvRoot(rootPath), outsidePath)
+import Debian.Repo.Internal.IO (buildArchOfRoot)
+import Debian.Repo.LocalRepository (copyLocalRepo, LocalRepository)
+import Debian.Repo.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Prelude (isSublistOf, replaceFile, rsync, sameInode, sameMd5sum)
+import Debian.Repo.Prelude.Verbosity (qPutStr, qPutStrLn, ePutStr, ePutStrLn, readProcFailing)
+import Debian.Repo.Repo (repoKey, repoURI)
+import Debian.Repo.Slice (NamedSliceList(sliceList), NamedSliceList(sliceListName), Slice(Slice, sliceRepoKey, sliceSource), SliceList(..))
+import Debian.Sources (DebSource(..), DebSource(sourceDist, sourceUri), SourceType(..), SourceType(..))
+import Debian.URI (uriToString')
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile, renameFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.Posix.Files (createLink, deviceID, fileID, FileStatus, modificationTime)
+import System.Process (shell)
+import System.Process.Chunks (collectProcessTriple, collectProcessResult)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import System.Unix.Mount (umountBelow)
+import Text.Regex (matchRegex, mkRegex)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- |This type represents an OS image located at osRoot built from a
+-- particular osBaseDistro using a particular osArch.  If an
+-- osLocalRepo argument is given, that repository will be copied into
+-- the environment and kept in sync, and lines will be added to
+-- sources.list to point to it.
+data OSImage
+    = OS { osRoot :: EnvRoot
+         , osBaseDistro :: NamedSliceList
+         , osArch :: Arch
+         , osLocalMaster :: LocalRepository
+	 -- ^ The associated local repository, where packages we build
+	 -- inside this image are first uploaded to.
+         , osLocalCopy :: LocalRepository
+	 -- ^ A copy of osLocalMaster located inside the os root environment.
+         , osSourcePackageCache :: Maybe [SourcePackage]
+         , osBinaryPackageCache :: Maybe [BinaryPackage]
+         }
+
+instance Ord OSImage where
+    compare a b = case compare (osRoot a) (osRoot b) of
+                    EQ -> case compare (osBaseDistro a) (osBaseDistro b) of
+                            EQ -> compare (osArch a) (osArch b)
+                            x -> x
+                    x -> x
+
+instance Eq OSImage where
+    a == b = compare a b == EQ
+
+-- |Create an OS image record
+createOSImage ::
+              EnvRoot			-- ^ The location where image is to be built
+           -> NamedSliceList		-- ^ The sources.list of the base distribution
+           -> LocalRepository           -- ^ The location of the local upload repository
+           -> IO OSImage
+createOSImage root distro repo =
+    do copy <- copyLocalRepo (EnvPath {envRoot = root, envPath = "/work/localpool"}) repo
+       -- At this point we can only support the build architecture of
+       -- the underlying system.  We can support multiple
+       -- distributions, but if the hardware is an amd64 the packages
+       -- produced will be amd64.
+       arch <- liftIO buildArchOfRoot
+       let os = OS { osRoot = root
+                   , osBaseDistro = distro
+                   , osArch = arch
+                   , osLocalMaster = repo
+                   , osLocalCopy = copy
+                   , osSourcePackageCache = Nothing
+                   , osBinaryPackageCache = Nothing }
+       return os
+
+-- | Create the OSImage record for a copy of an existing OSImage at a
+-- different location.
+cloneOSImage :: OSImage -> EnvRoot -> IO OSImage
+cloneOSImage src dst = do
+  copy <- copyLocalRepo (EnvPath {envRoot = dst, envPath = "/work/localpool"}) (osLocalMaster src)
+  return $ src {osRoot = dst, osLocalCopy = copy}
+
+-- | Set the location of the OSImage's root directory - where you
+-- would cd to before running chroot.
+-- chrootEnv :: OSImage -> EnvRoot -> OSImage
+-- chrootEnv os dst = os {osRoot = dst}
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+instance Show OSImage where
+    show os = intercalate " " ["OS {",
+                               rootPath (osRoot os),
+                               relName (sliceListName (osBaseDistro os)),
+                               show (osArch os),
+                               show (osLocalCopy os)]
+
+-- |The sources.list is the list associated with the distro name, plus
+-- the local sources where we deposit newly built packages.
+osFullDistro :: OSImage -> SliceList
+osFullDistro os =
+    let base = osBaseDistro os
+        repo' = osLocalCopy os
+        name = relName (sliceListName base)
+        localSources :: SliceList
+        localSources = SliceList {slices = [Slice {sliceRepoKey = repoKey repo', sliceSource = src},
+                                            Slice {sliceRepoKey = repoKey repo', sliceSource = bin}]}
+        src = DebSource Deb (repoURI repo') (Right (parseReleaseName name, [parseSection' "main"]))
+        bin = DebSource DebSrc (repoURI repo') (Right (parseReleaseName name, [parseSection' "main"])) in
+    SliceList { slices = slices (sliceList base) ++ slices localSources }
+
+data UpdateError
+    = Changed ReleaseName FilePath SliceList SliceList
+    | Missing ReleaseName FilePath
+    | Flushed
+
+instance Show UpdateError where
+    show (Changed r p l1 l2) = unwords ["Changed", show r, show p, ppDisplay l1, ppDisplay l2]
+    show (Missing r p) = unwords ["Missing", show r, show p]
+    show Flushed = "Flushed"
+
+syncOS' :: OSImage -> EnvRoot -> IO OSImage
+syncOS' src dst = do
+  mkdir
+  umount
+  (_result, _, _) <- rsync ["--exclude=/work/build/*"] (rootPath (osRoot src)) (rootPath dst)
+  cloneOSImage src dst
+    where
+      mkdir = createDirectoryIfMissing True (rootPath dst ++ "/work")
+      umount =
+          do srcResult <- umountBelow False (rootPath (osRoot src))
+             dstResult <- umountBelow False (rootPath dst)
+             case filter (\ (_, (code, _, _)) -> code /= ExitSuccess) (srcResult ++ dstResult) of
+               [] -> return ()
+               failed -> fail $ "umount failure(s): " ++ show failed
+
+-- | FIXME - we should notice the locale problem and run this.
+localeGen :: OSImage -> String -> IO ()
+localeGen os locale =
+    do let root = osRoot os
+       qPutStr ("Generating locale " ++  locale ++ " (" ++ stripDist (rootPath root) ++ ")...")
+       result <- try $ useEnv (rootPath root) forceList (readProcFailing (shell cmd) "")
+       case result of
+         Left (e :: SomeException) -> error $ "Failed to generate locale " ++ rootPath root ++ ": " ++ show e
+         Right output ->
+             case collectProcessTriple output of
+               (ExitSuccess, _, _) -> qPutStrLn "done"
+               (code, _, _) -> error $ "Failed to generate locale " ++ rootPath root ++ ": " ++ cmd ++ " -> " ++ show code
+    where
+      cmd = "locale-gen " ++ locale
+
+
+-- |To "neuter" an executable is to replace it with a hard link to
+-- \/bin\/true in such a way that the operation can be reversed.  This
+-- is done in order to make it safe to install files into it when it
+-- isn't "live".  If this operation fails it is assumed that the
+-- image is damaged, so it is removed.
+neuterEnv :: OSImage -> IO ()
+neuterEnv os =
+    do qPutStr ("Neutering OS image (" ++ stripDist root ++ ")...")
+       result <- try $ mapM_ (neuterFile os) neuterFiles
+       either (\ (e :: SomeException) -> error $ "Failed to neuter environment " ++ root ++ ": " ++ show e)
+              (\ _ -> qPutStrLn "done.")
+              result
+    where
+      root = rootPath (osRoot os)
+
+neuterFiles :: [(FilePath, Bool)]
+neuterFiles = [("/sbin/start-stop-daemon", True),
+	       ("/usr/sbin/invoke-rc.d", True),
+	       ("/sbin/init",False),
+	       ("/usr/sbin/policy-rc.d", False)]
+
+-- neuter_file from build-env.ml
+neuterFile :: OSImage -> (FilePath, Bool) -> IO ()
+neuterFile os (file, mustExist) =
+    do
+      -- putStrBl ("Neutering file " ++ file)
+      exists <- doesFileExist (outsidePath fullPath)
+      if exists then
+          neuterExistantFile else
+          if mustExist then
+              error ("Can't neuter nonexistant file: " ++ outsidePath fullPath) else
+              return () -- putStrBl "File doesn't exist, nothing to do"
+
+    where
+      neuterExistantFile =
+          do
+            sameFile <- sameInode (outsidePath fullPath) (outsidePath binTrue)
+            if sameFile then
+                return () else -- putStrBl "File already neutered"
+                neuterUnneuteredFile
+      neuterUnneuteredFile =
+          do
+            hasReal <- doesFileExist (outsidePath fullPath ++ ".real")
+            if hasReal then
+                neuterFileWithRealVersion else
+                neuterFileWithoutRealVersion
+            createLink (outsidePath binTrue) (outsidePath fullPath)
+      neuterFileWithRealVersion =
+          do
+            sameCksum <- sameMd5sum (outsidePath fullPath) (outsidePath fullPath ++ ".real")
+            if sameCksum then
+                removeFile (outsidePath fullPath) else
+                error (file ++ " and " ++ file ++ ".real differ (in " ++ rootPath root ++ ")")
+
+      neuterFileWithoutRealVersion = renameFile (outsidePath fullPath) (outsidePath fullPath ++ ".real")
+
+      fullPath = EnvPath root file
+      binTrue = EnvPath root "/bin/true"
+      root = osRoot os
+
+-- |Reverse the neuterEnv operation.
+restoreEnv :: OSImage -> IO OSImage
+restoreEnv os =
+    do
+      qPutStr "De-neutering OS image..."
+      result <- try $ mapM_ (restoreFile os) neuterFiles
+      either (\ (e :: SomeException) -> error $ "damaged environment " ++ rootPath root ++ ": " ++ show e ++ "\n  please remove it.")
+                 (\ _ -> return os) result
+    where
+      root = osRoot os
+
+-- check_and_restore from build-env.ml
+restoreFile :: OSImage -> (FilePath, Bool) -> IO ()
+restoreFile os (file, mustExist) =
+    do
+      exists <- doesFileExist (outsidePath fullPath)
+      if exists then
+          restoreExistantFile else
+          if mustExist then
+              error ("Can't restore nonexistant file: " ++ outsidePath fullPath) else
+              return ()
+    where
+      restoreExistantFile =
+          do
+            isTrue <- sameInode (outsidePath fullPath) (outsidePath binTrue)
+            hasReal <- doesFileExist (outsidePath fullPath ++ ".real")
+            case (isTrue, hasReal) of
+              (True, True) ->
+                  do
+                    removeFile (outsidePath fullPath)
+                    renameFile (outsidePath fullPath ++ ".real") (outsidePath fullPath)
+              (False, _) -> error "Can't restore file not linked to /bin/true"
+              (_, False) -> error "Can't restore file with no .real version"
+
+      fullPath = EnvPath root file
+      binTrue = EnvPath root "/bin/true"
+      root = osRoot os
+
+-- | Build the dependency relations for the build essential packages.
+-- For this to work the @build-essential@ package must be installed in
+-- the OSImage.
+buildEssential :: OSImage -> IO Relations
+buildEssential os = do
+      let root = osRoot os
+      -- qPutStrLn "Computing build essentials"
+      essential <-
+          readFile (rootPath root ++ "/usr/share/build-essential/essential-packages-list") >>=
+          return . lines >>= return . dropWhile (/= "") >>= return . tail >>= return . filter (/= "sysvinit") >>=
+          return . parseRelations . (intercalate ", ") >>=
+          return . (either (error "parse error in /usr/share/build-essential/essential-packages-list") id)
+      let re = mkRegex "^[^ \t]"
+      relationText <-
+          readFile (rootPath root ++ "/usr/share/build-essential/list") >>=
+          return . lines >>=
+          return . dropWhile (/= "BEGIN LIST OF PACKAGES") >>= return . tail >>=
+          return . takeWhile (/= "END LIST OF PACKAGES") >>=
+          return . filter ((/= Nothing) . (matchRegex re))
+      -- ePut ("buildEssentialText: " ++ intercalate ", " relationText)
+      let buildEssential'' = parseRelations (intercalate ", " relationText)
+      let buildEssential' = either (\ l -> error ("parse error in /usr/share/build-essential/list:\n" ++ show l)) id buildEssential''
+      return (essential ++ buildEssential')
+
+-- |Remove an image.  The removeRecursiveSafely function is used to
+-- ensure that any file systems mounted inside the image are unmounted
+-- and not destroyed.
+removeEnv :: OSImage -> IO ()
+removeEnv os =
+    do
+      ePutStr "Removing build environment..."
+      removeRecursiveSafely (rootPath root)
+      ePutStrLn "done."
+    where
+      root = osRoot os
+
+-- prefixes :: Maybe (L.ByteString, L.ByteString)
+-- prefixes = Just (" 1> ", " 2> ")
+
+stripDist :: FilePath -> FilePath
+stripDist path = maybe path (\ n -> drop (n + 7) path) (isSublistOf "/dists/" path)
+
+-- | This is a deepseq thing
+forceList :: [a] -> IO [a]
+forceList output = evaluate (length output) >> return output
+
+pbuilder :: FilePath
+         -> EnvRoot
+         -> NamedSliceList
+         -> LocalRepository
+         -> [String]
+         -> [String]
+         -> [String]
+         -> IO OSImage
+pbuilder top root distro repo _extraEssential _omitEssential _extra =
+      -- We can't create the environment if the sources.list has any
+      -- file:// URIs because they can't yet be visible inside the
+      -- environment.  So we grep them out, create the environment, and
+      -- then add them back in.
+    do ePutStrLn ("Creating clean build environment (" ++ relName (sliceListName distro) ++ ")")
+       ePutStrLn ("# " ++ cmd top)
+       let codefn ExitSuccess = return ()
+           codefn failure = error ("Could not create build environment:\n " ++ cmd top ++ " -> " ++ show failure)
+       liftIO (readProcFailing (shell (cmd top)) "") >>= return . collectProcessTriple >>= \ (result, _, _) -> codefn result
+       ePutStrLn "done."
+       os <- createOSImage root distro repo -- arch?  copy?
+       let sourcesPath' = rootPath root ++ "/etc/apt/sources.list"
+       -- Rewrite the sources.list with the local pool added.
+           sources = ppDisplay $ osFullDistro os
+       replaceFile sourcesPath' sources
+       return os
+    where
+      cmd x =
+          intercalate " " $ [ "pbuilder"
+                            , "--create"
+                            , "--distribution", (relName . sliceListName $ distro)
+                            , "--basetgz", x </> "pbuilderBase"
+                            , "--buildplace", rootPath root
+                            , "--preserve-buildplace"
+                            ]
+
+-- Create a new clean build environment in root.clean
+-- FIXME: create an ".incomplete" flag and remove it when build-env succeeds
+debootstrap
+    :: EnvRoot
+    -> NamedSliceList
+    -> LocalRepository
+    -> [String]
+    -> [String]
+    -> [String]
+    -> IO OSImage
+debootstrap root distro repo include exclude components =
+    do
+      ePutStr (unlines [ "Creating clean build environment (" ++ relName (sliceListName distro) ++ ")"
+                       , "  root: " ++ show root
+                       , "  baseDist: " ++ show baseDist
+                       , "  mirror: " ++ show mirror ])
+      -- We can't create the environment if the sources.list has any
+      -- file:// URIs because they can't yet be visible inside the
+      -- environment.  So we grep them out, create the environment, and
+      -- then add them back in.
+      readProcFailing (shell cmd) "" >>= codefn . fst . collectProcessResult
+      ePutStrLn "done."
+      os <- createOSImage root distro repo -- arch?  copy?
+      let sourcesPath' = rootPath root ++ "/etc/apt/sources.list"
+      -- Rewrite the sources.list with the local pool added.
+          sources = ppDisplay $ osFullDistro os
+      liftIO $ replaceFile sourcesPath' sources
+      return os
+    where
+      codefn ExitSuccess = return ()
+      codefn failure = error ("Could not create build environment:\n " ++ cmd ++ " -> " ++ show failure)
+
+      woot = rootPath root
+      wootNew = woot ++ ".new"
+      baseDist = either id (relName . fst) . sourceDist . sliceSource . head . slices . sliceList $ distro
+      mirror = uriToString' . sourceUri . sliceSource . head . slices . sliceList $ distro
+      cmd = intercalate " && "
+              ["set -x",
+               "rm -rf " ++ wootNew,
+               ("debootstrap " ++
+                (if include /= [] then "--include=" ++ intercalate "," include ++ " " else "") ++
+                (if exclude /= [] then "--exclude=" ++ intercalate "," exclude ++ " " else "") ++
+                "--variant=buildd " ++
+                "--components=" ++ intercalate "," components ++ " " ++
+                baseDist ++ " " ++
+                wootNew ++ " " ++
+                mirror),
+               "cat " ++ wootNew ++ "/etc/apt/sources.list | sed -e 's/^deb /deb-src /' >>" ++ wootNew ++ "/etc/apt/sources.list",
+               "mkdir -p " ++ woot,
+               "rm -rf " ++ woot,
+               "mv " ++ wootNew ++ " " ++ woot]
diff --git a/testdata/debian/Debian/Repo/Package.hs b/testdata/debian/Debian/Repo/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Package.hs
@@ -0,0 +1,54 @@
+-- | Relationships between debian source and binary packges.
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package
+    ( binaryPackageSourceVersion
+    , sourcePackageBinaryNames
+    ) where
+
+import Data.List as List (map)
+import qualified Data.Text as T (unpack)
+import Debian.Control (ControlFunctions(asString), formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.PackageID (PackageID(packageName, packageVersion))
+import Debian.Repo.PackageIndex (BinaryPackage(packageID, packageInfo), SourcePackage(sourceParagraph))
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = packageName . packageID $ package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
+
+-- | Return the names of the binary packages generated by this source package.
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
+    where
+      sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+      sourceBinaryNames paragraph =
+          case B.fieldValue "Binary" paragraph of
+            Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+            _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
diff --git a/testdata/debian/Debian/Repo/PackageID.hs b/testdata/debian/Debian/Repo/PackageID.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/PackageID.hs
@@ -0,0 +1,33 @@
+-- | The 'PackageID' type fully identifies a Debian package (source or
+-- binary, depending on the n type variable) by name and version.  This
+-- corresponds to Cabal's Distribution.Cabal.PackageIdentifier.
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.PackageID
+    ( PackageID(PackageID, packageVersion, packageName)
+    , prettyPackageID
+    , makeBinaryPackageID
+    , makeSourcePackageID
+    ) where
+
+import Data.Monoid ((<>))
+import Debian.Pretty (ppPrint)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName)
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Text.PrettyPrint.HughesPJClass (Doc, text)
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = ppPrint (packageName p) <> text "-" <> prettyDebianVersion (packageVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
diff --git a/testdata/debian/Debian/Repo/PackageIndex.hs b/testdata/debian/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,212 @@
+-- | The 'PackageIndex' type is the result of parsing a file
+-- containing control information about debian packages, either source
+-- or binary.  Examples of such files include
+-- @http://ftp.debian.org/debian/dists/sid/main/source/Sources.bz2@ or
+-- @http://ftp.debian.org/debian/dists/sid/main/binary-amd64/Packages.bz2@.
+{-# LANGUAGE FlexibleInstances, OverloadedStrings, StandaloneDeriving #-}
+module Debian.Repo.PackageIndex
+    ( PackageIndex(..)
+
+    , BinaryPackage(..)
+    , prettyBinaryPackage
+
+    , SourcePackage(..)
+    , SourceControl(..)
+    , SourceFileSpec(..)
+
+    , packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPaths
+    , packageIndexDirs
+    , packageIndexes
+    , sourceIndexes
+    , binaryIndexes
+    , releaseDir
+
+    , sortSourcePackages
+    , sortBinaryPackages
+    ) where
+
+import Data.List as List (sortBy, map, filter)
+import Data.Monoid ((<>))
+import Data.Set as Set (map, filter, toList, unions)
+import Data.Text (Text)
+import Debian.Arch (Arch(..), ArchOS(..), ArchCPU(..), prettyArch)
+import qualified Debian.Control.Text as T
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (Relations)
+import Debian.Pretty (PP(..), ppPrint)
+import Debian.Release (releaseName', Section(..), sectionName')
+import Debian.Repo.PackageID (PackageID(packageName, packageVersion), prettyPackageID)
+import Debian.Repo.Release (Release(..))
+import System.FilePath ((</>))
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.HughesPJClass (Doc, Pretty(pPrint), text)
+
+-- | A package index is identified by Section (e.g. main, contrib,
+-- non-free) and architecture (e.g. source, i386, amd64.)
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+instance Pretty (PP PackageIndex) where
+    pPrint (PP x) = ppPrint (packageIndexComponent x) <> text "_" <> ppPrint (packageIndexArch x)
+
+instance Pretty (PP Section) where
+    pPrint (PP (Section x)) = pPrint x
+
+instance Pretty (PP Arch) where
+    pPrint (PP Source) = text "source"
+    pPrint (PP All) = text "all"
+    pPrint (PP (Binary ArchOSAny ArchCPUAny)) = text "any"
+    pPrint (PP (Binary (ArchOS os) ArchCPUAny)) = ppPrint os
+    pPrint (PP (Binary ArchOSAny (ArchCPU cpu))) = ppPrint cpu
+    pPrint (PP (Binary (ArchOS os) (ArchCPU cpu))) = ppPrint (os <> "-" <> cpu)
+
+{-
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+-}
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      } deriving (Show)
+
+-- In a perfect world these would be valid Eq and Ord instances, but
+-- they are not because different builds of the same version can occur
+-- on different machines that are unaware of each other.  These should
+-- be removed.  But they are currently in use.
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage = prettyPackageID . packageID
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index </> packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release </> sectionName' (packageIndexComponent index) </> "source"
+      _ -> (releaseDir release </> sectionName' (packageIndexComponent index) </>
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists" </> (releaseName' . releaseName $ release)
+
+packageIndexPaths :: Release -> [FilePath]
+packageIndexPaths release = List.map (packageIndexPath release) . packageIndexes $ release
+
+packageIndexDirs :: Release -> [FilePath]
+packageIndexDirs release = List.map (packageIndexDir release) . packageIndexes $ release
+
+packageIndexes :: Release -> [PackageIndex]
+packageIndexes release = sourceIndexes release ++ binaryIndexes release
+
+sourceIndexes :: Release -> [PackageIndex]
+sourceIndexes release =
+    List.map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexes :: Release -> [PackageIndex]
+binaryIndexes release =
+    toList . unions . List.map componentIndexes $ releaseComponents release
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          Set.map archIndex (Set.filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+-- | Return a sorted list of available source packages, newest version first.
+sortSourcePackages :: [SrcPkgName] -> [SourcePackage] -> [SourcePackage]
+sortSourcePackages names pkgs =
+    sortBy cmp . filterNames $ pkgs
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          List.filter (flip elem names . packageName . sourcePackageID) packages
+      cmp p1 p2 =
+          compare v2 v1		-- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+sortBinaryPackages :: [BinPkgName] -> [BinaryPackage] -> [BinaryPackage]
+sortBinaryPackages names pkgs =
+    sortBy cmp . filterNames $ pkgs
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          List.filter (flip elem names . packageName . packageID) packages
+      cmp p1 p2 =
+          compare v2 v1		-- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
diff --git a/testdata/debian/Debian/Repo/Prelude.hs b/testdata/debian/Debian/Repo/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude.hs
@@ -0,0 +1,187 @@
+{-# LANGUAGE CPP, DeriveDataTypeable, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, OverloadedStrings,
+             PackageImports, ScopedTypeVariables, TemplateHaskell, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Debian.Repo.Prelude
+    ( countTasks
+    , nub'
+    , access
+    , (~=)
+    , (%=)
+    , symbol
+    , Debian.Repo.Prelude.Verbosity.readProcFailing -- re-export
+    , rsync
+    , partitionM
+    , maybeWriteFile
+    , replaceFile
+    , cond
+    , listIntersection
+    , sameInode
+    , sameMd5sum
+    , isSublistOf
+    , cd
+    , cartesianProduct
+    , writeFileIfMissing
+    , getSubDirectories
+    , dropPrefix
+    ) where
+
+import Control.Exception (Exception)
+import Control.Monad.State (get, modify, MonadIO, MonadState)
+import Control.Monad.Trans (liftIO)
+import Data.Lens.Lazy (getL, Lens, modL)
+import Data.List (group, sort)
+import Data.List as List (map)
+import Data.Monoid (mempty)
+import Data.Typeable (Typeable)
+import Debian.Repo.Prelude.Bool (cond)
+import Debian.Repo.Prelude.Files (getSubDirectories, maybeWriteFile, replaceFile, writeFileIfMissing)
+import Debian.Repo.Prelude.GPGSign (cd)
+import Debian.Repo.Prelude.List (cartesianProduct, dropPrefix, isSublistOf, listIntersection, partitionM)
+import Debian.Repo.Prelude.Misc (sameInode, sameMd5sum)
+import Debian.Repo.Prelude.Verbosity (ePutStrLn, readProcLazy', throwProcessResult'', readProcFailing)
+import Language.Haskell.TH (Exp(LitE), Lit(StringL), Name, nameBase, nameModule, Q)
+import System.Exit (ExitCode(..))
+import System.FilePath (dropTrailingPathSeparator)
+import System.Process (CreateProcess, proc)
+import System.Process.Chunks (collectProcessTriple)
+import System.Process.ListLike.LazyString ()
+import Text.Printf (printf)
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
+
+-- | This nub doesn't preserve order
+nub' :: (Ord a) => [a] -> [a]
+nub' = List.map head . group . sort
+
+access :: MonadState a m => Lens a b -> m b
+access l = get >>= return . getL l
+
+(~=) :: MonadState a m => Lens a b -> b -> m ()
+l ~= x = l %= const x
+
+-- | Modify a value.  (This is a version of Data.Lens.Lazy.%= that returns () instead of a.)
+(%=) :: MonadState a m => Lens a b -> (b -> b) -> m ()
+l %= f = modify (modL l f)
+
+-- | Build a string containing a symbol's fully qualified name (for debugging output.)
+symbol :: Name -> Q Exp
+symbol x = return $ LitE (StringL (maybe "" (++ ".") (nameModule x) ++ nameBase x))
+
+{-
+-- | Convenience function for running shell commands
+--    1. Runs quietly by default, but that can be controlled by the VERBOSITY environement variable
+--    2. If it exits with an error code all its output is echoed (with prefixes) and an exception is thrown
+--    3. The process input is the empty string
+--    4. When the output is echoed stdout is prefixed with "1>" and stderr with "2>".
+--    5. The output is a stream of 'Chunk'
+runProc :: MonadIO m => CreateProcess -> m [Chunk B.ByteString]
+-- runProc p = quieter 1 $ runProcessF prefixes p L.empty
+runProc p = liftIO $ readProcessChunks p L.empty >>= putIndented nl " 1> " " 2> " >>= mapM doChunk
+    where
+      nl :: Word8
+      nl = fromIntegral (ord '\n')
+      doChunk :: Chunk B.ByteString -> IO (Chunk B.ByteString)
+      doChunk (Result f@(ExitFailure _)) = error (showCmdSpecForUser (cmdspec p) ++ " -> " ++ show f)
+      doChunk x = return x
+      indent :: T.Text -> T.Text -> [T.Text]
+      indent pre text = map (pre <>) (T.lines text)
+
+-- | Like runProc, but does not raise an exception when process exit code is not 0.
+readProc :: MonadIO m => CreateProcess -> m [Chunk B.ByteString]
+readProc p = quieter 1 $ runProcess p L.empty
+-}
+
+rsync :: forall m. (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m (ExitCode, String, String)
+rsync extra source dest =
+    do let p = proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                             [dropTrailingPathSeparator source ++ "/",
+                              dropTrailingPathSeparator dest])
+       readProcLazy' p (mempty :: String) >>= liftIO . throwProcessResult'' throwRsyncError p >>= return . collectProcessTriple
+
+instance Exception RsyncError
+
+data RsyncError
+    = RsyncSuccess
+    | RsyncSyntaxOrUsage
+    | RsyncProtocolIncompatibility
+    | RsyncErrorsSelectingInputOutputFiles
+    | RsyncRequestedActionNotSupported
+    | RsyncErrorStartingClientServerProtocol
+    | RsyncDaemonUnableToAppendToLogfile
+    | RsyncErrorInSocketIO
+    | RsyncErrorInFileIO
+    | RsyncErrorInRsyncProtocolDataStream
+    | RsyncErrorsWithProgramDiagnostics
+    | RsyncErrorInIPCCode
+    | RsyncReceivedSIGUSR1orSIGINT
+    | RsyncSomeErrorReturnedByWaitpid
+    | RsyncErrorAllocatingCoreMemoryBuffers
+    | PartialTransferDueToError
+    | PartialTransferDueToVanishedSourceFiles
+    | TheMaxDeleteLimitStoppedDeletions
+    | TimeoutInDataSendReceive
+    | TimeoutWaitingForDaemonConnection
+    | RsyncUnexpected Int
+    deriving (Typeable, Show)
+
+rsyncError :: Int -> RsyncError
+rsyncError = fst . rsyncErrorInfo
+
+rsyncErrorMessage :: Int -> String
+rsyncErrorMessage = snd . rsyncErrorInfo
+
+rsyncErrorInfo :: Int -> (RsyncError, String)
+rsyncErrorInfo 1  = (RsyncSyntaxOrUsage, "rsync: Syntax or usage error")
+rsyncErrorInfo 2  = (RsyncProtocolIncompatibility, "rsync: Protocol incompatibility")
+rsyncErrorInfo 3  = (RsyncErrorsSelectingInputOutputFiles, "rsync: Errors selecting input/output files, dirs")
+rsyncErrorInfo 4  = (RsyncRequestedActionNotSupported, "rsync: Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server.")
+rsyncErrorInfo 5  = (RsyncErrorStartingClientServerProtocol, "rsync: Error starting client-server protocol")
+rsyncErrorInfo 6  = (RsyncDaemonUnableToAppendToLogfile, "rsync: Daemon unable to append to log-file")
+rsyncErrorInfo 10 = (RsyncErrorInSocketIO, "rsync: Error in socket I/O")
+rsyncErrorInfo 11 = (RsyncErrorInFileIO, "rsync: Error in file I/O")
+rsyncErrorInfo 12 = (RsyncErrorInRsyncProtocolDataStream, "rsync: Error in rsync protocol data stream")
+rsyncErrorInfo 13 = (RsyncErrorsWithProgramDiagnostics, "rsync: Errors with program diagnostics")
+rsyncErrorInfo 14 = (RsyncErrorInIPCCode, "rsync: Error in IPC code")
+rsyncErrorInfo 20 = (RsyncReceivedSIGUSR1orSIGINT, "rsync: Received SIGUSR1 or SIGINT")
+rsyncErrorInfo 21 = (RsyncSomeErrorReturnedByWaitpid, "rsync: Some error returned by waitpid()")
+rsyncErrorInfo 22 = (RsyncErrorAllocatingCoreMemoryBuffers, "rsync: Error allocating core memory buffers")
+rsyncErrorInfo 23 = (PartialTransferDueToError, "Partial transfer due to error")
+rsyncErrorInfo 24 = (PartialTransferDueToVanishedSourceFiles, "Partial transfer due to vanished source files")
+rsyncErrorInfo 25 = (TheMaxDeleteLimitStoppedDeletions, "rsync: The --max-delete limit stopped deletions")
+rsyncErrorInfo 30 = (TimeoutInDataSendReceive, "rsync: Timeout in data send/receive")
+rsyncErrorInfo 35 = (TimeoutWaitingForDaemonConnection, "rsync: Timeout waiting for daemon connection")
+rsyncErrorInfo n = (RsyncUnexpected n, "Unexpected rsync error: " ++ show n)
+
+throwRsyncError :: CreateProcess -> ExitCode -> Maybe RsyncError
+throwRsyncError _ ExitSuccess = Nothing
+throwRsyncError _ (ExitFailure n) = Just $ fst $ rsyncErrorInfo n
+{-
+    case n of
+      1 -> error "rsync: Syntax or usage error"
+      2 -> error "rsync: Protocol incompatibility"
+      3 -> error "rsync: Errors selecting input/output files, dirs"
+      4 -> error "rsync: Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+      5 -> error "rsync: Error starting client-server protocol"
+      6 -> error "rsync: Daemon unable to append to log-file"
+      10 -> error "rsync: Error in socket I/O"
+      11 -> error "rsync: Error in file I/O"
+      12 -> error "rsync: Error in rsync protocol data stream"
+      13 -> error "rsync: Errors with program diagnostics"
+      14 -> error "rsync: Error in IPC code"
+      20 -> error "rsync: Received SIGUSR1 or SIGINT"
+      21 -> error "rsync: Some error returned by waitpid()"
+      22 -> error "rsync: Error allocating core memory buffers"
+      23 -> error "Partial transfer due to error"
+      24 -> error "Partial transfer due to vanished source files"
+      25 -> error "rsync: The --max-delete limit stopped deletions"
+      30 -> error "rsync: Timeout in data send/receive"
+      35 -> error "rsync: Timeout waiting for daemon connection"
+      _ -> error $ "rsync: Unexpected failure " ++ show n
+-}
diff --git a/testdata/debian/Debian/Repo/Prelude/Bool.hs b/testdata/debian/Debian/Repo/Prelude/Bool.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Bool.hs
@@ -0,0 +1,7 @@
+module Debian.Repo.Prelude.Bool
+    ( cond
+    ) where
+
+cond :: a -> a -> Bool -> a
+cond t _ True = t
+cond _ f False = f
diff --git a/testdata/debian/Debian/Repo/Prelude/Chroot.hs b/testdata/debian/Debian/Repo/Prelude/Chroot.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Chroot.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
+-- | This module, except for useEnv, is copied from the build-env package.
+module Debian.Repo.Prelude.Chroot
+    ( fchroot
+    , useEnv
+    -- , forceList  -- moved to progress
+    -- , forceList'
+    ) where
+
+import Control.Exception (finally, evaluate)
+
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString.Lazy.Char8 as L
+import Debian.Repo.Prelude.Verbosity (readProc)
+import Foreign.C.Error
+import Foreign.C.String
+import System.Directory (createDirectoryIfMissing)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath (dropTrailingPathSeparator, dropFileName)
+import System.IO (hPutStr, stderr)
+import System.IO.Error (catchIOError)
+import System.Posix.Env (getEnv)
+import System.Posix.IO
+import System.Posix.Directory
+import System.Process (readProcess, readProcessWithExitCode, showCommandForUser, proc)
+
+foreign import ccall unsafe "chroot" c_chroot :: CString -> IO Int
+
+{-# DEPRECATED forceList "If you need forceList enable it in progress-System.Unix.Process." #-}
+forceList = undefined
+{-# DEPRECATED forceList' "If you need forceList' enable it in progress-System.Unix.Process." #-}
+forceList' = undefined
+
+-- |chroot changes the root directory to filepath
+-- NOTE: it does not change the working directory, just the root directory
+-- NOTE: will throw IOError if chroot fails
+chroot :: FilePath -> IO ()
+chroot fp = withCString fp $ \cfp -> throwErrnoIfMinus1_ "chroot" (c_chroot cfp)
+
+-- |fchroot runs an IO action inside a chroot
+-- fchroot performs a chroot, runs the action, and then restores the
+-- original root and working directory. This probably affects the
+-- chroot and working directory of all the threads in the process,
+-- so...
+-- NOTE: will throw IOError if internal chroot fails
+fchroot :: FilePath -> IO a -> IO a
+fchroot path action =
+    do origWd <- getWorkingDirectory
+       rootFd <- openFd "/" ReadOnly Nothing defaultFileFlags
+       chroot path
+       changeWorkingDirectory "/"
+       action `finally` (breakFree origWd rootFd)
+    where
+      breakFree origWd rootFd =
+          do changeWorkingDirectoryFd rootFd
+             closeFd rootFd
+             chroot "."
+             changeWorkingDirectory origWd
+
+-- | The ssh inside of the chroot needs to be able to talk to the
+-- running ssh-agent.  Therefore we mount --bind the ssh agent socket
+-- dir inside the chroot (and umount it when we exit the chroot.
+useEnv :: FilePath -> (a -> IO a) -> IO a -> IO a
+useEnv rootPath force action =
+    do -- In order to minimize confusion, this QIO message is output
+       -- at default quietness.  If you want to suppress it while seeing
+       -- the output from your action, you need to say something like
+       -- quieter (+ 1) (useEnv (quieter (\x->x-1) action))
+       sockPath <- getEnv "SSH_AUTH_SOCK"
+       home <- getEnv "HOME"
+       copySSH home
+       -- We need to force the output before we exit the changeroot.
+       -- Otherwise we lose our ability to communicate with the ssh
+       -- agent and we get errors.
+       withSock sockPath . fchroot rootPath $ (action >>= force)
+    where
+      copySSH Nothing = return ()
+      copySSH (Just home) = do
+        -- Do NOT preserve ownership, files must be owned by root.
+        createDirectoryIfMissing True (rootPath ++ "/root")
+        readProc (proc "/usr/bin/rsync" ["-rlptgDHxS", "--delete", home ++ "/.ssh/", rootPath ++ "/root/.ssh"]) "" `catchIOError` (\ e -> error $ "Failure in useEnv:copySSH - " ++ show e)
+        return ()
+
+      withSock Nothing action = action
+      withSock (Just sockPath) action =
+          withMountBind dir (rootPath ++ dir) action
+          where dir = dropTrailingPathSeparator (dropFileName sockPath)
+      withMountBind toMount mountPoint action =
+          (do createDirectoryIfMissing True mountPoint
+              readProc (proc "/bin/mount" ["--bind", toMount, mountPoint]) "" `catchIOError` (\ e -> error $ "Failure mounting in useEnv:withMountBind - " ++ show e)
+              action) `finally` do readProc (proc "/bin/umount" [mountPoint]) "" `catchIOError` (\ e -> error $ "Failure unmounting in useEnv:withMountBind - " ++ show e)
+                                   return ()
+
+{-
+      run cmd args =
+          do (code, out, err) <- readProcessWithExitCode cmd args ""
+             case code of
+               ExitSuccess -> return ()
+               _ -> error ("Exception in System.Unix.Chroot.useEnv: " ++ showCommandForUser cmd args ++ " -> " ++ show code ++
+                           "\n\nstdout:\n " ++ prefix "> " out ++ "\n\nstderr:\n" ++ prefix "> " err)
+-}
+      prefix pre s = unlines (map (pre ++) (lines s))
+
+{-
+printDots :: Int -> [Output] -> IO [Output]
+printDots cpd output =
+    foldM f 0 output >> return output
+    where
+      print rem (Stdout s) =
+          let (dots, rem') = quotRem (rem + length s) in
+          hPutStr stderr (replicate dots '.')
+          return rem'
+      print rem (Stderr s) = print rem (Stdout s)
+-}
diff --git a/testdata/debian/Debian/Repo/Prelude/Files.hs b/testdata/debian/Debian/Repo/Prelude/Files.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Files.hs
@@ -0,0 +1,253 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-- |Some extra operations on files.  The functions here generally
+-- return (Right ()) on success, Left [messages] on failure, and throw
+-- an exception when a failure leaves things in an inconsistant state.
+-- An example of an inconsistant state would be if we got a failure
+-- when writing out a file, but were unable to restore the original
+-- file to its original position.
+module Debian.Repo.Prelude.Files
+    ( getSubDirectories
+    , renameAlways
+    , renameMissing
+    , deleteMaybe
+    , installFiles
+    , writeAndZipFileWithBackup
+    , writeAndZipFile
+    , backupFile
+    , writeFileIfMissing
+    , maybeWriteFile		-- writeFileUnlessSame
+    , createSymbolicLinkIfMissing
+    , prepareSymbolicLink
+    , forceRemoveLink
+    , replaceFile
+    ) where
+
+import qualified Codec.Compression.GZip as GZip
+import qualified Codec.Compression.BZip as BZip
+import		 Control.Exception as E
+import		 Control.Monad
+import qualified Data.ByteString.Lazy as B
+import		 Data.List
+import		 Data.Maybe
+import Debian.Repo.Prelude.Misc (parentPath)
+import		 System.Unix.Directory
+import		 System.Directory
+import		 System.IO.Error
+import		 System.Posix.Files
+
+-- | Return the list of subdirectories, omitting . and .. and ignoring
+-- symbolic links.
+getSubDirectories :: FilePath -> IO [String]
+getSubDirectories path =
+    getDirectoryContents path >>=
+    return . filter (not . (flip elem) [".", ".."]) >>=
+    filterM isRealDirectory
+    where
+      isRealDirectory name = getSymbolicLinkStatus (path ++ "/" ++ name) >>= return . not . isSymbolicLink
+
+-- |Atomically install a list of files.  Returns a list of what went
+-- wrong on failure.  Will throw an error if it fails and is unable to
+-- restore the original files to their original states.
+installFiles :: [(FilePath, FilePath)] -> IO (Either [String] ())
+installFiles pairs =
+    do backedUp <- mapM (uncurry renameAlways) (zip originalFiles backupFiles)
+       case lefts backedUp of
+         [] -> 
+             do renamed <- mapM (uncurry renameAlways) (zip replacementFiles originalFiles)
+                case lefts renamed of
+                  [] -> return $ Right ()
+                  _ ->
+                      -- We failed after all the original files were
+                      -- renamed and maybe some of the replacement
+                      -- files were installed.  Move all the renamed
+                      -- files back into place.
+                      do restored <- mapM (uncurry renameAlways) (zip backupFiles originalFiles)
+                         case lefts restored of
+	                   -- We succeeded in failing.
+                           [] -> return . Left . concat . lefts $ renamed
+	                   -- Restore failed.  Throw an exception.
+                           _ -> error ("installFiles: Couldn't restore original files after write failure:" ++
+                                       concat (map message (zip3 replacementFiles originalFiles renamed)) ++
+                                       concat (map message (zip3 originalFiles backupFiles restored)))
+         _ ->
+             -- We failed after renaming all original files, but
+             -- before any of the replacement files were installed.
+             -- Restore the backup for any missing original files.
+             do restored <- mapM (uncurry renameMissing) (zip backupFiles originalFiles)
+                case lefts restored of
+	          -- We succeeded in failing.
+                  [] -> return . Left . concat . lefts $ backedUp
+		  -- Restore failed.  Throw an exception.
+                  _ -> error ("installFiles: Couldn't restore original files after write failure: " ++
+                              concat (map message (zip3 originalFiles backupFiles backedUp)) ++
+                              concat (map message (zip3 backupFiles originalFiles restored)))
+    where
+      replacementFiles = map fst pairs
+      originalFiles = map snd pairs
+      backupFiles = map (++ "~") originalFiles
+      message (path1, path2, Left problems) =
+          "\n  " ++ path1 ++ " -> " ++ path2 ++ ": " ++ concat (intersperse ", " (map show problems))
+      message (_, _, Right ()) = ""
+
+lefts :: [Either a b] -> [a]
+lefts xs = catMaybes $ map (either Just (const Nothing)) xs
+
+-- |Change a file's name only if the new name doesn't exist.
+renameMissing :: FilePath -> FilePath -> IO (Either [String] ())
+renameMissing old new =
+    do exists <- fileExist new
+       case exists of
+         True -> return $ Right ()
+         False -> renameAlways old new
+
+-- |Change a file's name, removing any existing file with the new name.
+renameAlways :: FilePath -> FilePath -> IO (Either [String] ())
+renameAlways old new =
+    do deleted <- deleteMaybe new
+       case deleted of
+         Right () ->
+             E.try (rename old new) >>=
+             return . either (\ (e :: SomeException) -> Left ["Couldn't rename " ++ old ++ " -> " ++ new ++ ": " ++ show e]) (\ _ -> Right ())
+         x -> return x
+
+-- |Change a file's name if it exists.
+renameMaybe :: FilePath -> FilePath -> IO (Either [String] ())
+renameMaybe old new =
+    do exists <- fileExist old
+       case exists of
+         False -> return $ Right ()
+         True -> renameAlways old new
+
+-- |Delete a file if it exists
+deleteMaybe :: FilePath -> IO (Either [String] ())
+deleteMaybe path =
+    do exists <- fileExist path
+       case exists of
+         False -> return $ Right ()
+         True ->
+             do status <- getSymbolicLinkStatus path
+		-- To do: should we remove the directory contents?
+                let rm = if isDirectory status then removeDirectory else removeLink
+                try (rm path) >>= return . either (\ (e :: SomeException) -> Left ["Couldn't remove " ++ path ++ ": " ++ show e]) (const . Right $ ())
+
+-- |Create or update gzipped and bzip2-ed versions of a file.
+zipFile :: FilePath -> IO (Either [String] ())
+zipFile path =
+    try (do forceRemoveLink gz
+            forceRemoveLink bz2
+            B.readFile path >>= B.writeFile gz . {- t1 . -} GZip.compress
+            B.readFile path >>= B.writeFile bz2 . {- t2 . -} BZip.compress) >>=
+    return . either (\ (e :: SomeException) -> Left ["Failure writing and zipping " ++ path, show e]) Right
+    where
+      gz = path ++ ".gz"
+      bz2 = path ++ ".bz2"
+      --t1 s = trace ("Size of " ++ gz ++ " text: " ++ show (L.length s)) s
+      --t2 s = trace ("Size of " ++ bz2 ++ " text: " ++ show (L.length s)) s
+      --writeMessage (command, output) =
+      --    case exitCodeOnly output of
+      --      (ExitFailure n : _) ->
+      --          command ++ " -> " ++ show n ++ ":\n  " ++ L.unpack (stderrOnly output)
+      --      _ -> ""
+
+-- |like removeLink, but does not fail if link did not exist
+forceRemoveLink :: FilePath -> IO ()
+forceRemoveLink fp = removeLink fp `E.catch` (\e -> unless (isDoesNotExistError e) (ioError e))
+
+-- | Write out three versions of a file, regular, gzipped, and bzip2ed.
+writeAndZipFileWithBackup :: FilePath -> B.ByteString -> IO (Either [String] ())
+writeAndZipFileWithBackup path text =
+    backupFile path >>=
+    either (\ e -> return (Left ["Failure renaming " ++ path ++ " -> " ++ path ++ "~: " ++ show e]))
+           (\ _ -> try (B.writeFile path text) >>=
+                   either (\ (e :: SomeException) ->
+                               restoreBackup path >>=
+                               either (\ e' -> error ("Failed to restore backup: " ++ path ++ "~ -> " ++ path ++ ": " ++ show e'))
+                                      (\ _ -> return (Left ["Failure writing " ++ path ++ ": " ++ show e])))
+                          (\ _ -> zipFile path))
+
+-- | Write out three versions of a file, regular, gzipped, and bzip2ed.
+-- This new version assumes the files are written to temporary locations,
+-- so any existing file there can be removed.
+writeAndZipFile :: FilePath -> B.ByteString -> IO (Either [String] ())
+writeAndZipFile path text =
+    deleteMaybe path >>=
+    either (\ e -> return (Left ["Failure removing " ++ path ++ ": " ++ show e]))
+           (\ _ -> try (B.writeFile path text) >>=
+                   either (\ (e :: SomeException) -> return (Left ["Failure writing " ++ path ++ ": " ++ show e]))
+                          (\ _ -> zipFile path))
+
+-- Turn a file into a backup file if it exists.
+backupFile :: FilePath -> IO (Either [String] ())
+backupFile path = renameMaybe path (path ++ "~")
+
+restoreBackup :: FilePath -> IO (Either [String] ())
+restoreBackup path = renameMaybe (path ++ "~") path
+
+-- | Like writeFile, but if the file already exists don't touch it.
+-- Example: writeFileIfMissing True \"\/var\/lib\/dpkg\/status\" \"\"
+writeFileIfMissing :: Bool -> FilePath -> String -> IO ()
+writeFileIfMissing mkdirs path text =
+    do
+      exists <- doesFileExist path
+      case exists of
+        False ->
+            do
+              if mkdirs then
+                  createDirectoryIfMissing True (parentPath path) else
+                  return ()
+              replaceFile path text
+        True ->
+            return ()
+
+-- | Write a file if its content is different from the given text.
+maybeWriteFile :: FilePath -> String -> IO ()
+maybeWriteFile path text =
+    try (readFile path) >>= maybeWrite
+    where
+      maybeWrite (Left (e :: IOException)) | isDoesNotExistError e = writeFile path text
+      maybeWrite (Left e) = error ("maybeWriteFile: " ++ show e)
+      maybeWrite (Right old) | old == text = return ()
+      maybeWrite (Right _old) = 
+          --hPutStrLn stderr ("Old text: " ++ show old) >>
+          --hPutStrLn stderr ("New text: " ++ show text) >>
+          replaceFile path text
+
+-- |Add-on for System.Posix.Files
+createSymbolicLinkIfMissing :: String -> FilePath -> IO ()
+createSymbolicLinkIfMissing text path =
+    try (getSymbolicLinkStatus path) >>=
+    either (\ (_ :: SomeException) -> createSymbolicLink text path) (\ _ -> return ())
+
+prepareSymbolicLink :: FilePath -> FilePath -> IO ()
+prepareSymbolicLink name path =
+    checkExists >>= checkType >>= checkContent
+    where
+      checkExists = doesDirectoryExist path >>= orCreate
+      checkType False = return False
+      checkType True = getSymbolicLinkStatus path >>= return . isSymbolicLink >>= orReplace
+      checkContent False = return ()
+      checkContent True = readSymbolicLink path >>= return . (== name) >>= orReplace >> return ()
+      orReplace True = return True
+      orReplace False = do removeRecursiveSafely path; orCreate False
+      orCreate True = return True
+      orCreate False = do createSymbolicLink name path; return False
+
+-- Replace a file's contents, accounting for the possibility that the
+-- old contents of the file may still be being read.  Apparently there
+-- is a race condition in the file system so we may get one or more
+-- isAlreadyBusyError exceptions before the writeFile succeeds.
+replaceFile :: FilePath -> String -> IO ()
+replaceFile path text =
+    --tries 100 10 f	-- There is now a fix for this problem, see ghc ticket 2122.
+    f
+    where
+      f :: IO ()
+      f = removeFile path `E.catch` (\ e -> if isDoesNotExistError e then return () else ioError e) >> writeFile path text
+
+-- Try something n times, returning the first Right or the last Left
+-- if it never succeeds.  Sleep between tries.
+--tries :: Int -> Int -> (IO a) -> IO (Either Exception a)
+{-
+tries _ 1 f = try f >>= either (return . Left) (return . Right)
+tries usec count f = try f >>= either (\ (_ :: SomeException) -> usleep usec >> tries usec (count - 1) f) (return . Right)
+-}
diff --git a/testdata/debian/Debian/Repo/Prelude/GPGSign.hs b/testdata/debian/Debian/Repo/Prelude/GPGSign.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/GPGSign.hs
@@ -0,0 +1,72 @@
+module Debian.Repo.Prelude.GPGSign
+    ( sign
+    , PGPKey(..)
+    , pgpSignFiles
+    , pgpSignFile
+    , cd
+    ) where
+
+import Debian.Repo.Prelude.Misc (cd)
+import System.Process
+import System.IO
+import System.Exit
+
+_test :: PGPKey'' -> [FilePath] -> IO [FilePath]
+_test key files =
+    mapM (sign key) files
+
+type PGPKey'' = String
+
+
+sign :: PGPKey'' -> FilePath -> IO FilePath
+sign keyname path =
+    do (_, _,err,pid) <- runInteractiveProcess cmd args workingDir noEnv
+       status <- waitForProcess pid
+       case status of
+         ExitSuccess -> return outputPath
+         ExitFailure _ ->
+             do gpgerr <- hGetContents err
+                hPutStr stderr gpgerr
+                exitWith status
+       where
+         cmd = "/usr/bin/gpg"
+         args = [ "--batch"
+                , "--yes"
+                , "--default-key", keyname
+                , "-o", outputPath
+                , "--clearsign"
+                , path
+                ]
+         outputPath = path ++ ".gpg"
+         workingDir = Nothing -- Just (dirName path)
+         noEnv = Nothing
+
+data PGPKey = Key String | Default deriving Show
+
+pgpSignFiles :: FilePath -> PGPKey -> [FilePath] -> IO [Bool]
+pgpSignFiles root key files = cd root $ mapM (pgpSignFile key) files
+
+pgpSignFile :: PGPKey -> FilePath -> IO Bool
+pgpSignFile keyname path =
+    do (_, _,err,pid) <- runInteractiveProcess cmd args workingDir noEnv
+       status <- waitForProcess pid
+       case status of
+         ExitSuccess -> return True
+         ExitFailure _code ->
+             do gpgerr <- hGetContents err
+                hPutStr stderr gpgerr
+                return False
+       where
+         cmd = "/usr/bin/gpg"
+         args = defaultKey ++
+             [ "--batch"
+             , "--yes"
+             , "-o", outputPath
+             , "--armor"
+             , "--detach-sign"
+             , path
+             ]
+         defaultKey = case keyname of Key name -> ["--default-key", name]; Default -> []
+         outputPath = path ++ ".gpg"
+         workingDir = Nothing -- Just (dirName path)
+         noEnv = Nothing
diff --git a/testdata/debian/Debian/Repo/Prelude/List.hs b/testdata/debian/Debian/Repo/Prelude/List.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/List.hs
@@ -0,0 +1,95 @@
+module Debian.Repo.Prelude.List
+    ( consperse
+    , surround
+    , changePrefix
+    , dropPrefix
+    , cartesianProduct
+    , wordsBy
+    , empty
+    , sortByMapped
+    , sortByMappedM
+    , partitionM
+    , listIntersection
+    , isSublistOf
+    ) where
+
+import Control.Monad
+import Data.List
+
+{-# DEPRECATED consperse "Use intercalate" #-}
+consperse :: [a] -> [[a]] -> [a]
+-- ^ The mighty consperse function - e.g. consperse "," ["a", "b"] -> "a,b"
+-- consperse = MissingH.List.join
+consperse s l = concat . intersperse s $ l
+
+surround :: [a] -> [a] -> [[a]] -> [a]
+-- ^ surround each element of a list - e.g. surround "(" ")" ["a", "b"] -> ["(a)(b)"]
+surround prefix suffix items = concat $ map ((prefix ++) . (++ suffix)) items
+
+-- |Replace the prefix of s, return Nothing if it doesn't match.
+changePrefix :: (Eq a) => [a] -> [a] -> [a] -> Maybe [a]
+changePrefix old new s = maybe Nothing (Just . (new ++)) (dropPrefix old s)
+
+-- |Remove a prefix of s, return nothing if it doesn't match.
+dropPrefix :: (Eq a) => [a] -> [a] -> Maybe [a]
+dropPrefix prefix s =
+    case isPrefixOf prefix s of
+      True -> Just (drop (length prefix) s)
+      False -> Nothing
+
+cartesianProduct :: [[a]] -> [[a]]
+-- ^ cartesianProduct [[1,2,3], [4,5],[6]] -> [[1,4,6],[1,5,6],[2,4,6],[2,5,6],[3,4,6],[3,5,6]]
+cartesianProduct [] = []
+cartesianProduct [xs] = map (: []) xs
+cartesianProduct (xs : yss) =
+    distribute xs (cartesianProduct yss)
+    where distribute xs' yss' = concat (map (\ x -> map (x :) yss') xs')
+
+-- |FIXME: implement for a string
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s = 
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+-- |Like maybe, but with empty vs. non-empty list
+empty :: b -> ([a] -> b) -> [a] -> b
+empty e _ [] = e
+empty _ f l = f l
+
+-- |Sort a list using the compare function on the list elements mapped
+-- over f.  This is like "sortBy (\ a b -> compare (f a) (f b))"
+-- except that f is applied O(n) times instead of O(n log n)
+sortByMapped :: (a -> b) -> (b -> b -> Ordering) -> [a] -> [a]
+sortByMapped f cmp list =
+    map fst sorted
+    where
+      sorted = sortBy (\ (_, x) (_, y) -> cmp x y) pairs
+      pairs = zip list (map f list)
+
+-- |Monadic version of sortByMapped
+sortByMappedM :: (a -> IO b) -> (b -> b -> Ordering) -> [a] -> IO [a]
+sortByMappedM f cmp list =
+    do
+      pairs <- mapM f list >>= return . (zip list)
+      let sorted = sortBy (\ (_, x) (_, y) -> cmp x y) pairs
+      return (map fst sorted)
+
+partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])
+partitionM p xs =
+    foldM f ([], []) xs
+    where f (a, b) x = p x >>= (\ flag -> return $ if flag then (x : a, b) else (a, x : b))
+
+listIntersection :: Eq a => [[a]] -> [a]
+listIntersection [] = []
+listIntersection (first : rest) = foldr intersect first rest
+
+isSublistOf :: Eq a => [a] -> [a] -> Maybe Int
+isSublistOf sub lst =
+    maybe Nothing (\ s -> Just (length s - length sub))
+              (find (isSuffixOf sub) (inits lst))
+
+{-
+lookups :: (Eq a) => a -> [(a, b)] -> [b]
+lookups a = map snd . filter ((a ==) . fst)
+-}
diff --git a/testdata/debian/Debian/Repo/Prelude/Lock.hs b/testdata/debian/Debian/Repo/Prelude/Lock.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Lock.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Debian.Repo.Prelude.Lock
+    ( withLock
+    , awaitLock
+    ) where
+
+import Control.Exception
+import Control.Monad.RWS
+import System.Directory
+import System.IO
+import System.IO.Error
+import System.Posix.Files
+import System.Posix.IO
+import System.Posix.Unistd
+
+withLock :: (MonadIO m) => FilePath -> m a -> m a
+withLock path task =
+    liftIO (checkLock >> takeLock) >> task >>= \ result -> liftIO dropLock >> return result
+    where
+      -- Return True if file is locked by a running process, false otherwise
+      --checkLock :: IO (Either Exception ())
+      checkLock = readFile path `catch` checkReadError >>= processRunning . lines
+      checkReadError :: IOError -> IO String
+      checkReadError e | isDoesNotExistError e = return ""
+      checkReadError e = throw e
+      processRunning :: [String] -> IO ()
+      processRunning (pid : _) =
+          do exists <- doesDirectoryExist ("/proc/" ++ pid)
+             case exists of
+               True -> throw (lockedBy pid path)
+               False -> breakLock
+      processRunning [] = breakLock
+      breakLock = removeFile path `catch` checkBreakError
+      checkBreakError (e :: IOException) | isDoesNotExistError e = return ()
+      checkBreakError e = throw e
+      takeLock :: IO ()
+      takeLock =
+          -- Try to create the lock file in exclusive mode, if this
+          -- succeeds then we have a lock.  Then write the process ID
+          -- into the lock and close.
+          openFd path ReadWrite (Just 0o600) (defaultFileFlags {exclusive = True, trunc = True}) >>=
+          fdToHandle >>= \ h -> processID >>= hPutStrLn h >> hClose h
+      dropLock = removeFile path `catch` checkDrop
+      checkDrop (e :: IOException) | isDoesNotExistError e = return ()
+      checkDrop e = throw e
+
+-- |Like withLock, but instead of giving up immediately, try n times
+-- with a wait between each.
+awaitLock :: Int -> Int -> FilePath -> IO a -> IO a
+awaitLock tries usecs path task =
+    attempt 0
+    where
+      attempt n | n >= tries = error "awaitLock: too many failures"
+      attempt n = withLock path task `catch` checkLockError
+          where
+            checkLockError e | isAlreadyInUseError e = liftIO (usleep usecs) >> attempt (n + 1)
+            checkLockError e = throw e
+
+processID :: IO String
+processID = readSymbolicLink "/proc/self"
+
+lockedBy :: String -> FilePath -> IOError
+lockedBy pid path = mkIOError alreadyInUseErrorType ("Locked by " ++ pid) Nothing (Just path)
diff --git a/testdata/debian/Debian/Repo/Prelude/Misc.hs b/testdata/debian/Debian/Repo/Prelude/Misc.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Misc.hs
@@ -0,0 +1,156 @@
+module Debian.Repo.Prelude.Misc
+    (
+    -- * String functions
+      columns
+    , justify
+    -- * Tuple functions
+    , mapSnd
+    -- * FilePath functions
+    , parentPath
+    , canon
+    -- * Map and Set functions
+    , listMap
+    , listDiff
+    -- * System.Posix
+    , checkSuperUser
+    , md5sum
+    , sameInode
+    , sameMd5sum
+    , tarDir
+    -- * Processes
+    -- , splitOutput
+    -- * ByteString
+    , cd
+    -- * Debugging
+    , read'
+    ) where
+
+import		 Control.Exception
+import qualified Data.ByteString.Lazy.Char8 as B
+import qualified Data.Digest.Pure.MD5
+import		 Data.List
+import qualified Data.Map as Map
+import		 Data.Maybe
+import qualified Data.Set as Set
+import		 Debian.Repo.Prelude.List
+import           System.Exit
+import		 System.FilePath
+import		 System.Directory
+import		 System.Posix.Files
+import		 System.Posix.User (getEffectiveUserID)
+import           System.Process (readProcessWithExitCode)
+-- import System.Process.Progress (keepStdout, keepStderr, keepResult)
+import		 Text.Regex
+
+mapSnd :: (b -> c) -> (a, b) -> (a, c)
+mapSnd f (a, b) = (a, f b)
+
+-- Control file stuff
+
+-- |Pad strings so the columns line up. The argument and return value
+-- elements are the rows of a table.  Do not pad the rightmost column.
+columns :: [[String]] -> [[String]]
+columns rows =
+    map (map pad . zip (widths ++ [0])) rows
+    where
+      widths = map (fromJust . listMax) . transpose . map (map length . init) $ rows
+      listMax l = foldl (\ a b -> Just . maybe b (max b) $ a) Nothing l
+      pad (width, field) = field ++ replicate (max 0 (width - length field)) ' '
+
+-- |Group words into lines of length n or less.
+justify :: String -> Int -> [[String]]
+justify s n =
+    foldr doWord [[]] (words s)
+    where doWord w [] = [[w]]
+	  doWord w (ws : etc) = 
+	      if length (concat (intersperse " " (w:ws))) <= n then
+		 (w : ws) : etc else
+		 [w] : ws : etc
+
+-- |dirname
+parentPath :: FilePath -> FilePath
+parentPath path = fst (splitFileName path)
+
+-- |Turn a list of (k, a) pairs into a map from k -> [a].  The order of the elements in
+-- the a list is preserved.
+listMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
+listMap pairs =
+    foldl insertPair Map.empty (reverse pairs)
+    where insertPair m (k,a) = Map.insert k (a : (Map.findWithDefault [] k m)) m
+
+-- Return the difference of two lists.  Order is not preserved.
+listDiff :: Ord a => [a] -> [a] -> [a]
+listDiff a b = Set.toList (Set.difference (Set.fromList a) (Set.fromList b))
+
+-- | Weak attempt at canonicalizing a file path.
+canon :: FilePath -> FilePath
+canon path =
+    let re = mkRegex "/" in
+    let names = splitRegex re path in
+    concat (intersperse "/" (merge names))
+    where
+      merge (".." : xs) = ".." : (merge xs)
+      merge ("." : xs) = "." : (merge xs)
+      merge (_ : ".." : xs) = (merge xs)
+      merge (x : "." : xs) = (merge (x : xs))
+      merge (x : xs) = x : merge xs
+      merge [] = []
+
+{-# DEPRECATED md5sum "Use Data.ByteString.Lazy.Char8.readFile path >>= return . show . Data.Digest.Pure.MD5.md5" #-}
+-- | Run md5sum on a file and return the resulting checksum as text.
+md5sum :: FilePath -> IO String
+md5sum path = B.readFile path >>= return . show . Data.Digest.Pure.MD5.md5
+
+-- | Predicate to decide if two files have the same inode.
+sameInode :: FilePath -> FilePath -> IO Bool
+sameInode a b =
+    do
+      aStatus <- getFileStatus a
+      bStatus <- getFileStatus b
+      return (deviceID aStatus == deviceID bStatus && fileID aStatus == fileID bStatus)
+
+-- | Predicate to decide if two files have the same md5 checksum.
+sameMd5sum :: FilePath -> FilePath -> IO Bool
+sameMd5sum a b =
+    do
+      asum <- md5sum a
+      bsum <- md5sum b
+      return (asum == bsum)
+
+{-
+splitOutput :: [Output B.ByteString] -> (B.ByteString, B.ByteString, [ExitCode])
+splitOutput output = (B.concat (keepStdout output), B.concat (keepStderr output), keepResult output)
+-}
+
+-- |A version of read with a more helpful error message.
+read' :: Read a => String -> a
+read' s =
+    case reads s of
+      [] -> error $ "read - no parse: " ++ show s
+      ((x, _s) : _) -> x
+
+checkSuperUser :: IO Bool
+checkSuperUser = getEffectiveUserID >>= return . (== 0)
+
+-- | Given a tarball, return the name of the top directory.
+tarDir :: FilePath -> IO (Maybe String)
+tarDir path =
+    readProcessWithExitCode "tar" ["tfz", path] "" >>= \ (code, out, _) ->
+    case code of
+      ExitSuccess -> return . dir . lines $ out
+      _ -> return Nothing
+    where
+      dir [] = Nothing
+      dir (file : _) = case wordsBy (== '/') file of
+                         [] -> Nothing
+                         ("" : _) -> Nothing
+                         (s : _) -> Just s
+
+cd :: FilePath -> IO a -> IO a
+cd name m = 
+    bracket 
+        (do cwd <- getCurrentDirectory
+            setCurrentDirectory name
+            return cwd)
+        (\oldwd -> do setCurrentDirectory oldwd)
+        (const m)
diff --git a/testdata/debian/Debian/Repo/Prelude/Process.hs b/testdata/debian/Debian/Repo/Prelude/Process.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Process.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -Wall #-}
+module Debian.Repo.Prelude.Process
+    ( readProcLazy
+    , readProcLazy'
+    , throwProcessResult'
+    , throwProcessResult''
+    , throwProcessFailure
+    , mapResultM
+    , testExit
+    , processException
+    , readProcFailing
+    , insertProcessEnv
+    , modifyProcessEnv
+    ) where
+
+import Control.Arrow (second)
+import Control.Exception (Exception)
+import Control.Monad (when)
+import Control.Monad.Catch (MonadMask, bracket)
+import Control.Monad.Trans (MonadIO, liftIO)
+import Data.ByteString.Lazy as L (ByteString)
+import Data.String (IsString)
+import System.IO (hPutStr, hPutStrLn, stderr)
+import System.Posix.Env (setEnv, getEnv, unsetEnv)
+
+import Control.Exception (evaluate)
+import Data.Time (NominalDiffTime, getCurrentTime, diffUTCTime)
+import Debian.Repo.Prelude.Verbosity
+import Control.Exception (throw)
+import GHC.IO.Exception (IOErrorType(OtherError))
+import Language.Haskell.TH (Loc)
+import Language.Haskell.TH.Syntax (Lift)
+import System.Environment (getEnvironment)
+import System.Exit (ExitCode(..))
+import System.IO.Error (mkIOError)
+import System.Process (CreateProcess(cmdspec, cwd, env))
+import System.Process.Chunks (Chunk(..), putMappedChunks, showCmdSpecForUser, readCreateProcessChunks)
+import System.Process.ListLike (ListLikeLazyIO)
+
+-- | Verbosity enabled process reader.  (Why MonadIO and not IO?)
+readProcLazy :: MonadIO m => Loc -> CreateProcess -> L.ByteString -> m [Chunk L.ByteString]
+readProcLazy p input = do
+  v <- verbosity
+  case v of
+    n | n <= 0 -> liftIO $ readCreateProcessChunks p input
+    1 -> liftIO $ readCreateProcessChunks p input >>= putMappedChunks (insertCommandStart p . filter (not . isOutput))
+    _ -> liftIO $ readCreateProcessChunks p input >>= putIndentedShowCommand p " 1> " " 1> "
+
+putIndentedShowCommand :: (ListLikeLazyIO a c, Eq c, IsString a) =>
+                          CreateProcess -> String -> String -> [Chunk a] -> IO [Chunk a]
+putIndentedShowCommand p outp errp chunks =
+    putMappedChunks (insertCommandDisplay p . indentChunks outp errp) chunks
+
+-- | Insert a chunk displaying the command and its arguments at the
+-- beginning of the chunk list.
+insertCommandStart :: (IsString a, ListLikeLazyIO a c, Eq c) =>
+                      CreateProcess -> [Chunk a] -> [Chunk a]
+insertCommandStart p chunks = [Stderr (fromString (" -> " ++ showCreateProcessForUser p ++ "\n"))] <> chunks
+
+-- | Insert a chunk displaying the command and the result code.
+insertCommandResult :: (IsString a, ListLikeLazyIO a c, Eq c) =>
+                       CreateProcess -> [Chunk a] -> [Chunk a]
+insertCommandResult _ [] = []
+insertCommandResult p (Result code : xs) =
+    Stderr (fromString (" <- " ++ show code ++ " <- " ++ showCmdSpecForUser (cmdspec p) ++ "\n")) :
+    Result code :
+    xs
+insertCommandResult p (x : xs) = x : insertCommandResult p xs
+
+insertCommandDisplay :: (IsString a, ListLikeLazyIO a c, Eq c) => CreateProcess -> [Chunk a] -> [Chunk a]
+insertCommandDisplay p = insertCommandResult p . insertCommandStart p
+
+-- | Verbosity enabled process reader.  (Why MonadIO and not IO?)
+readProcLazy' :: (ListLikeLazyIO a c, IsString a, Eq c, MonadIO m) => CreateProcess -> a -> m [Chunk a]
+readProcLazy' p input = liftIO $ do
+  v <- verbosity
+  case v of
+    n | n <= 0 -> readCreateProcessChunks p input
+    1 -> readCreateProcessChunks p input >>= putMappedChunks (insertCommandStart p . filter (not . isOutput))
+    _ -> readCreateProcessChunks p input >>= putIndentedShowCommand p " 1> " " 1> "
+
+-- | Copied from "System.Process", the exception thrown when the
+-- process started by 'System.Process.readProcess' gets an
+-- 'ExitFailure'.
+processException :: CreateProcess -> ExitCode -> IOError
+processException p code =
+    mkIOError OtherError (showCmdSpecForUser (cmdspec p) ++ maybe "" (\ d -> "(in " ++ show d ++ ")") (cwd p) ++ " -> " ++ show code) Nothing Nothing
+
+-- | Verbosity enabled process reader that throws an exception on ExitFailure.
+readProcFailing :: MonadIO m => CreateProcess -> L.ByteString -> m [Chunk L.ByteString]
+readProcFailing p input = readProcLazy p input >>= liftIO . throwProcessFailure p
+
+-- | Set an environment variable in the CreateProcess, initializing it
+-- with what is in the current environment.
+insertProcessEnv :: [(String, String)] -> CreateProcess -> IO CreateProcess
+insertProcessEnv pairs = modifyProcessEnv (map (second Just) pairs)
+
+instance Lift Loc where
+    lift x = recConE 'Loc [ (,) <$> (pure 'loc_filename) <*> litE (stringL (loc_filename x))
+                          , (,) <$> (pure 'loc_package) <*> litE (stringL (loc_package x))
+                          , (,) <$> (pure 'loc_module) <*> litE (stringL (loc_module x))
+                          , (,) <$> (pure 'loc_start) <*> [|($(litE (integerL (fromIntegral (fst (loc_start x))))),
+                                                             $(litE (integerL (fromIntegral (snd (loc_start x)))))) :: (Int, Int)|]
+                          , (,) <$> (pure 'loc_end) <*> [|($(litE (integerL (fromIntegral (fst (loc_end x))))),
+                                                           $(litE (integerL (fromIntegral (snd (loc_end x)))))) :: (Int, Int)|] ]
+
+-- | Embed an expression of type Loc containing the location
+-- information for the place where it appears.  Could be used in
+-- custom Exception types and similar:
+--
+-- > throw $ MyException $LOC
+__LOC__ :: Q Exp
+__LOC__ = lift =<< location
+
+throwProcessResult' :: (ExitCode -> Maybe IOError) -> CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessResult' f p chunks = mapResultM (\ code -> maybe (return $ Result code) (throw $ processException p code) (f code)) chunks
+
+throwProcessResult'' :: Exception e => (CreateProcess -> ExitCode -> Maybe e) -> CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessResult'' f p chunks = mapResultM (\ code -> maybe (return $ Result code) throw (f p code)) chunks
+
+throwProcessFailure :: CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessFailure p = throwProcessResult' (testExit Nothing (Just . processException p . ExitFailure)) p
+
+testExit :: a -> (Int -> a) -> ExitCode -> a
+testExit s _ ExitSuccess = s
+testExit _ f (ExitFailure n) = f n
+
+modifyProcessEnv :: [(String, Maybe String)] -> CreateProcess -> IO CreateProcess
+modifyProcessEnv pairs p = do
+  env0 <- maybe getEnvironment return (env p)
+  let env' = foldl modEnv1 env0 pairs
+  return $ p {env = Just env'}
+
+modEnv1 :: [(String, String)] -> (String, Maybe String) -> [(String, String)]
+modEnv1 env0 (name, mvalue) = maybe [] (\ v -> [(name, v)]) mvalue ++ filter ((/= name) . fst) env0
diff --git a/testdata/debian/Debian/Repo/Prelude/SSH.hs b/testdata/debian/Debian/Repo/Prelude/SSH.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/SSH.hs
@@ -0,0 +1,116 @@
+-- | Set up or verify SSH access to a remove machine.
+module Debian.Repo.Prelude.SSH
+    ( sshVerify
+    , sshExportDeprecated
+    , sshCopy
+    ) where
+
+import System.Directory
+import System.Posix.User
+import System.Environment
+import System.Exit
+import System.IO
+import System.Process (system, readProcessWithExitCode, showCommandForUser)
+
+-- |Set up access to destination (user\@host).
+sshExportDeprecated :: String -> Maybe Int -> IO (Either String ())
+sshExportDeprecated dest port =
+    generatePublicKey >>=
+    either (return . Left) (testAccess dest port) >>=
+    either (return . Left) (openAccess dest port)
+
+-- parseURI "ssh://dsf@server:22"
+-- URI {uriScheme = "ssh:", uriAuthority = Just (URIAuth {uriUserInfo = "dsf@", uriRegName = "server", uriPort = ":22"}), uriPath = "", uriQuery = "", uriFragment = ""}
+
+-- |Make sure there is a public key for the local account
+generatePublicKey :: IO (Either String FilePath)
+generatePublicKey =
+    do user <- getEffectiveUserID
+       home <- getUserEntryForID user >>= return . homeDirectory
+       let cmd = "yes '' | ssh-keygen -t rsa 2>&1 >/dev/null"
+       let keypath = home ++ "/.ssh/id_rsa.pub"
+       exists <- doesFileExist keypath
+       case exists of
+         True -> return . Right $ keypath
+         False ->
+             do hPutStrLn stderr $ "generatePublicKey " ++ " -> " ++ keypath
+                code <- system cmd
+                case code of
+                  ExitFailure n ->
+                      return . Left $ "Failure: " ++ show cmd ++ " -> " ++ show n
+                  _ -> return . Right $ keypath
+
+-- |See if we already have access to the destination (user\@host).
+sshVerify :: String -> Maybe Int -> IO (Either String ())
+sshVerify dest port =
+    do r@(result, _out, _err) <- readProcessWithExitCode cmd args ""
+       case result of
+         ExitSuccess -> return (Right ())		-- We do
+         ExitFailure _ ->
+             hPutStrLn stderr (showCommandForUser cmd args ++ " -> " ++ show r) >> return (Left (show r))	-- We do not
+    where
+      cmd = "ssh"
+      args = (["-o", "PreferredAuthentications hostbased,publickey"] ++ maybe [] (\ n -> ["-p", show n]) port ++ [dest, "pwd"])
+      _sshTestCmd dest' port' =
+          ("ssh -o 'PreferredAuthentications hostbased,publickey' " ++
+           (maybe "" (("-p " ++) . show) port') ++ " " ++ show dest' ++ " pwd > /dev/null && exit 0")
+
+testAccess :: String -> Maybe Int -> FilePath -> IO (Either String (Maybe FilePath))
+testAccess dest port keypath =
+    sshVerify dest port >>= return . either Left (\ () -> Right (Just keypath))
+
+-- |Try to set up the keys so we have access to the account.  I don't
+-- think we use this any more, and I don't think you should either.
+openAccess :: String -> Maybe Int -> Maybe FilePath -> IO (Either String ())
+openAccess _ _ Nothing = return . Right $ ()
+openAccess dest port (Just keypath) =
+    do hPutStrLn stderr $ "openAccess " ++ show dest ++ " " ++ show port ++ " " ++ show keypath
+       let args = maybe [] (\ x -> ["-p", show x]) port  ++ [show dest, sshOpenRemoteCmd]
+       (code, out, err) <- readFile keypath >>= readProcessWithExitCode "ssh" args
+       case code of
+         ExitFailure n -> return . Left $ "Failure: " ++ showCommandForUser "ssh" args ++ " -> " ++ show n ++
+	                                  "\n\nstdout: " ++ out ++ "\n\nstderr: " ++ err
+         _ -> return . Right $ ()
+    where
+      sshOpenRemoteCmd =
+          ("chmod g-w . && " ++				-- Ssh will not work if the permissions aren't just so
+           "chmod o-rwx . && " ++
+           "mkdir -p .ssh && " ++
+           "chmod 700 .ssh && " ++
+           "cat >> .ssh/authorized_keys2 && " ++	-- Add the key to the authorized key list
+           "chmod 600 .ssh/authorized_keys2")
+
+-- This used to be main.
+{-
+test =
+    getDest >>=
+    either (return . Left) (uncurry sshExport) >>=
+    either (error . show) (const . exitWith $ ExitSuccess)
+
+-- |Get the destination account info from the command line
+getDest :: IO (Either String (String, Maybe Int))
+getDest =
+    getArgs >>= checkArgs
+    where checkArgs [dest] =
+              return $ case parseURI ("ssh://" ++ dest) of
+                         Just (URI {uriAuthority = Just (URIAuth {uriUserInfo = user, uriRegName = host, uriPort = ""})}) ->
+                             Right (user ++ host, Nothing)
+                         Just (URI {uriAuthority = Just (URIAuth {uriUserInfo = user, uriRegName = host, uriPort = port})}) ->
+                             case reads (dropWhile (== ':') port) :: [(Int, String)] of
+                               [] -> Left $ "Invalid destination: " ++ dest ++ " (" ++ port ++ ")"
+                               ((n, _) : _) -> Right (user ++ host, Just n)
+                         _ -> Left $ "Invalid destination: " ++ dest
+          checkArgs args = return . Left $ "Usage: sshexport user@host"
+-}
+
+-- |Copy the ssh configuration from $HOME to the \/root directory of a
+-- changeroot.
+sshCopy :: FilePath -> IO ExitCode
+sshCopy root =
+    do exists <- doesDirectoryExist "~/.ssh"
+       home <- getEnv "HOME"
+       case exists of
+         True -> system ("mkdir -p " ++ root ++ "/root && " ++
+                         "rsync -aHxSpDt --delete " ++ home ++ "/.ssh/ " ++ root ++ "/root/.ssh && " ++
+                         "chown -R root.root " ++ root ++ "/root/.ssh")
+         False -> system "mkdir -p /root/.ssh"
diff --git a/testdata/debian/Debian/Repo/Prelude/Time.hs b/testdata/debian/Debian/Repo/Prelude/Time.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Time.hs
@@ -0,0 +1,62 @@
+module Debian.Repo.Prelude.Time
+    ( formatDebianDate
+    , myTimeDiffToString
+    ) where
+
+import Control.Exception
+import Data.List
+import Data.Time
+import System.Locale
+import System.Time
+import Text.Printf
+
+{- This function is so complicated because there seems to be no way
+   to get the Data.Time to format seconds without the fractional part,
+   which seems not to be allowed in the RFC822 format.
+   The mysterious 'take 2' pulls in just the integral part.
+
+   Note the getCurrentTime :: IO UTCTime, so this will always be in UTC time
+   with the resulting string ending in "UTC".
+ -}
+
+
+formatDebianDate :: FormatTime t => t -> [Char]
+formatDebianDate t =
+    prefix ++ seconds ++ suffix
+        where prefix = formatTime defaultTimeLocale prefixFormat t
+              seconds = take 2 $ formatTime defaultTimeLocale secondsFormat t
+              suffix = formatTime defaultTimeLocale suffixFormat t
+              prefixFormat = "%a, %d %b %Y %H:%M:"
+              secondsFormat = "%S"
+              suffixFormat = " %Z"
+              format = "%a, %d %b %Y %H:%M:%S %Z"
+              _test = assert (format == prefixFormat ++ secondsFormat ++ suffixFormat)
+
+_test :: IO Bool
+_test =
+    do tz <- getCurrentTimeZone
+       let ut = localTimeToUTC tz testtime
+       return $ teststring == formatDebianDate ut
+    where testtime = LocalTime {localDay=fromGregorian testyear testmonth testday,
+                                localTimeOfDay=TimeOfDay{todHour=testhour,todMin=testminute,todSec=testsecond}}
+          testyear = 2006
+          testmonth = 12
+          testday = 19
+          testhour = 12
+          testminute = 19
+          testsecond = 15.29
+          teststring = "Tue, 19 Dec 2006 17:19:15 UTC"
+
+myTimeDiffToString :: TimeDiff -> String
+myTimeDiffToString diff =
+    do
+      case () of
+        _ | isPrefixOf "00:00:0" s -> drop 7 s ++ printf ".%03d" ms ++ " s."
+        _ | isPrefixOf "00:00:" s -> drop 6 s ++ printf ".%03d" ms ++ " s."
+        _ | isPrefixOf "00:" s -> drop 3 s
+        _ -> s
+    where
+      s = formatTimeDiff defaultTimeLocale "%T" diff
+      ms = ps2ms
+      ps2ms = quot (ps + 500000000) 1000000000
+      ps = tdPicosec diff
diff --git a/testdata/debian/Debian/Repo/Prelude/Verbosity.hs b/testdata/debian/Debian/Repo/Prelude/Verbosity.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Prelude/Verbosity.hs
@@ -0,0 +1,166 @@
+{-# OPTIONS_GHC -Wall #-}
+module Debian.Repo.Prelude.Verbosity
+    ( modifyEnv
+    , qPutStr
+    , qPutStrLn
+    , ePutStr
+    , ePutStrLn
+    , timeTask
+    , quieter
+    , noisier
+    , withModifiedVerbosity
+    , defaultVerbosity
+    , readProcLazy
+    , readProcLazy'
+    , throwProcessResult'
+    , throwProcessResult''
+    , throwProcessFailure
+    , mapResultM
+    , testExit
+    , processException
+    , readProcFailing
+    , insertProcessEnv
+    , modifyProcessEnv
+    ) where
+
+import Control.Arrow (second)
+import Control.Exception (Exception)
+import Control.Monad (when)
+import Control.Monad.Catch (MonadMask, bracket)
+import Control.Monad.Trans (MonadIO, liftIO)
+import Data.ByteString.Lazy as L (ByteString)
+import Data.String (IsString)
+import System.IO (hPutStr, hPutStrLn, stderr)
+import System.Posix.Env (setEnv, getEnv, unsetEnv)
+
+import Control.Exception (evaluate)
+import Data.Time (NominalDiffTime, getCurrentTime, diffUTCTime)
+
+import Control.Exception (throw)
+import GHC.IO.Exception (IOErrorType(OtherError))
+import System.Environment (getEnvironment)
+import System.Exit (ExitCode(..))
+import System.IO.Error (mkIOError)
+import System.Process (CreateProcess(cmdspec, cwd, env))
+import System.Process.Chunks (Chunk(..), insertCommandStart, putIndentedShowCommand, putMappedChunks, showCmdSpecForUser, readCreateProcessChunks)
+import System.Process.ListLike (ListLikeLazyIO)
+
+-- | Generalization of Posix setEnv/unSetEnv.
+modifyEnv :: String -> (Maybe String -> Maybe String) -> IO ()
+modifyEnv name f =
+    getEnv name >>= maybe (unsetEnv name) (\ x -> setEnv name x True) . f
+
+ePutStr :: MonadIO m => String -> m ()
+ePutStr = liftIO . hPutStr stderr
+ePutStrLn :: MonadIO m => String -> m ()
+ePutStrLn = liftIO . hPutStrLn stderr
+
+qPutStr :: MonadIO m => String -> m ()
+qPutStr s = do
+  v <- verbosity
+  when (v > 0) (ePutStr s)
+
+qPutStrLn :: MonadIO m => String -> m ()
+qPutStrLn s = do
+  v <- verbosity
+  when (v > 0) (ePutStrLn s)
+
+-- | Run a task and return the elapsed time along with its result.
+timeTask :: IO a -> IO (a, NominalDiffTime)
+timeTask x =
+    do start <- getCurrentTime
+       result <- x >>= evaluate
+       finish <- getCurrentTime
+       return (result, diffUTCTime finish start)
+
+quieter :: (MonadIO m, MonadMask m) => Int -> m a -> m a
+quieter n action = withModifiedVerbosity (\ v -> v - n) action
+
+noisier :: (MonadIO m, MonadMask m) => Int -> m a -> m a
+noisier n action = withModifiedVerbosity (\ v -> v + n) action
+
+withModifiedVerbosity :: (MonadIO m, MonadMask m) => (Int -> Int) -> m a -> m a
+withModifiedVerbosity f action =
+    bracket verbosity -- acquire
+            (\ v0 -> liftIO (modifyEnv "VERBOSITY" (const (Just (show v0))))) -- release
+            (\ v0 -> liftIO (modifyEnv "VERBOSITY" (const (Just (show (f v0))))) >> action)
+
+defaultVerbosity :: Int
+defaultVerbosity = 0
+
+verbosity :: MonadIO m => m Int
+verbosity = liftIO $ getEnv "VERBOSITY" >>= return . maybe 1 read
+
+-- | Verbosity enabled process reader.  (Why MonadIO and not IO?)
+readProcLazy :: MonadIO m => CreateProcess -> L.ByteString -> m [Chunk L.ByteString]
+readProcLazy p input = do
+  v <- verbosity
+  case v of
+    n | n <= 0 -> liftIO $ readCreateProcessChunks p input
+    1 -> liftIO $ readCreateProcessChunks p input >>= putMappedChunks (insertCommandStart p . filter (not . isOutput))
+    _ -> liftIO $ readCreateProcessChunks p input >>= putIndentedShowCommand p " 1> " " 1> "
+
+isOutput :: Chunk a -> Bool
+isOutput (Stdout _) = True
+isOutput (Stderr _) = True
+isOutput _ = False
+
+-- | Verbosity enabled process reader.  (Why MonadIO and not IO?)
+readProcLazy' :: (ListLikeLazyIO a c, IsString a, Eq c, MonadIO m) => CreateProcess -> a -> m [Chunk a]
+readProcLazy' p input = liftIO $ do
+  v <- verbosity
+  case v of
+    n | n <= 0 -> readCreateProcessChunks p input
+    1 -> readCreateProcessChunks p input >>= putMappedChunks (insertCommandStart p . filter (not . isOutput))
+    _ -> readCreateProcessChunks p input >>= putIndentedShowCommand p " 1> " " 1> "
+
+throwProcessResult' :: (ExitCode -> Maybe IOError) -> CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessResult' f p chunks = mapResultM (\ code -> maybe (return $ Result code) (throw $ processException p code) (f code)) chunks
+
+throwProcessResult'' :: Exception e => (CreateProcess -> ExitCode -> Maybe e) -> CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessResult'' f p chunks = mapResultM (\ code -> maybe (return $ Result code) throw (f p code)) chunks
+
+throwProcessFailure :: CreateProcess -> [Chunk a] -> IO [Chunk a]
+throwProcessFailure p = throwProcessResult' (testExit Nothing (Just . processException p . ExitFailure)) p
+
+mapResultM :: Monad m => (ExitCode -> m (Chunk a)) -> [Chunk a] -> m [Chunk a]
+mapResultM f chunks = mapM (\ x -> case x of Result code -> f code; _ -> return x) chunks
+
+testExit :: a -> (Int -> a) -> ExitCode -> a
+testExit s _ ExitSuccess = s
+testExit _ f (ExitFailure n) = f n
+
+-- | Copied from "System.Process", the exception thrown when the
+-- process started by 'System.Process.readProcess' gets an
+-- 'ExitFailure'.
+processException :: CreateProcess -> ExitCode -> IOError
+processException p code =
+    mkIOError OtherError (showCmdSpecForUser (cmdspec p) ++ maybe "" (\ d -> "(in " ++ show d ++ ")") (cwd p) ++ " -> " ++ show code) Nothing Nothing
+
+-- | Verbosity enabled process reader that throws an exception on ExitFailure.
+readProcFailing :: MonadIO m => CreateProcess -> L.ByteString -> m [Chunk L.ByteString]
+readProcFailing p input = readProcLazy p input >>= liftIO . throwProcessFailure p
+
+-- | Set an environment variable in the CreateProcess, initializing it
+-- with what is in the current environment.
+insertProcessEnv :: [(String, String)] -> CreateProcess -> IO CreateProcess
+insertProcessEnv pairs = modifyProcessEnv (map (second Just) pairs)
+{-
+insertEnv pairs p = do
+    pairs' <- maybe (getEnvironment >>= return . (++ pairs)) return (env p)
+    return p {env = Just pairs'}
+
+modEnv :: [(String, Maybe String)] -> [(String, String)] -> [(String, String)]
+modEnv [] env0 = env0
+modEnv pairs env0 = foldl modEnv1 env0 pairs
+-- foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
+-}
+
+modEnv1 :: [(String, String)] -> (String, Maybe String) -> [(String, String)]
+modEnv1 env0 (name, mvalue) = maybe [] (\ v -> [(name, v)]) mvalue ++ filter ((/= name) . fst) env0
+
+modifyProcessEnv :: [(String, Maybe String)] -> CreateProcess -> IO CreateProcess
+modifyProcessEnv pairs p = do
+  env0 <- maybe getEnvironment return (env p)
+  let env' = foldl modEnv1 env0 pairs
+  return $ p {env = Just env'}
diff --git a/testdata/debian/Debian/Repo/Release.hs b/testdata/debian/Debian/Repo/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Release.hs
@@ -0,0 +1,130 @@
+-- | A release is a named collection of package indexes, e.g. sid.
+{-# LANGUAGE FlexibleInstances, PackageImports, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , parseComponents
+    , parseArchitectures
+    , parseReleaseFile
+    , getReleaseInfoRemote
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (ErrorCall(ErrorCall), Exception(toException), SomeException, try)
+import Control.Monad.Trans (liftIO)
+import Data.List (intercalate)
+import Data.Maybe (catMaybes)
+import Data.Set (Set, fromList)
+import Data.Text (Text, unpack)
+import qualified Data.Text as T (Text, unpack)
+import qualified Data.Text.IO as T (readFile)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (Control'(Control), fieldValue, Paragraph, Paragraph', parseControl)
+import Debian.Repo.Prelude.Verbosity (qPutStr)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName(..), releaseName', Section(..))
+import Debian.URI (dirFromURI, fileFromURI, URI(uriPath), uriToString')
+import Debian.UTF8 as Deb (decode)
+import Prelude hiding (readFile)
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+data Source = LocalPath FilePath | RemotePath URI
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
+
+-- | A Debian Release is a named set of packages such as Jessie
+-- (Debian version 7.0) or Precise (Ubuntu version 12.04.)  The
+-- information here is obtained from the Release file in
+-- dists/releasename.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: Set Arch
+                       -- ^ e.g. amd64, i386, arm, etc, the set of architectures for this release - when a binary
+                       -- package has architecture "all" or a source package has architecture "any" it means this.
+                       , releaseComponents :: [Section] -- ^ Typically main, contrib, non-free
+                       } deriving (Eq, Ord, Read, Show)
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path' dist aliases =
+    liftIO (readFile path') >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> File Text -> Release
+parseRelease dist aliases file =
+    case text file of
+      Failure msgs -> error $ "Could not read " ++ show (path file) ++ ": " ++ show msgs
+      Success t ->
+          case T.parseControl (show (path file)) t of
+            Left msg -> error $ "Failure parsing " ++ show (path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (File {path = path file, text = Success info}) dist aliases
+
+-- | Turn a parsed Release file into a Release
+makeReleaseInfo :: File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(File {text = Failure msgs}) _dist _aliases =
+    error $ "Failure reading " ++ show (path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(File {text = Success info}) dist aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = dist
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (path file)
+
+parseArchitectures :: Text -> Set Arch
+parseArchitectures archList =
+    fromList . map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    dirFromURI distsURI >>=
+    either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: File (T.Paragraph' Text) -> Maybe Text
+      getSuite (File {text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (File {text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ File {path = RemotePath releaseURI, text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
diff --git a/testdata/debian/Debian/Repo/RemoteRepository.hs b/testdata/debian/Debian/Repo/RemoteRepository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/RemoteRepository.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.RemoteRepository
+    ( RemoteRepository(..)
+    ) where
+
+import Debian.Pretty (PP(..), ppPrint)
+import Debian.Repo.Release (Release)
+import Debian.Repo.Repo (Repo(repoKey, repoReleaseInfo), RepoKey(Remote))
+import Debian.URI (fromURI', URI')
+import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), text)
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show, Eq, Ord)
+
+instance Repo RemoteRepository where
+    repoKey (RemoteRepository uri _) = Remote uri
+    repoReleaseInfo (RemoteRepository _ info) = info
+
+-- | URI has a bogus show function, which we are using here.
+instance Pretty (PP URI') where
+    pPrint = text . show . fromURI' . unPP
+
+instance Pretty (PP RemoteRepository) where
+    pPrint (PP (RemoteRepository s _)) = ppPrint s
diff --git a/testdata/debian/Debian/Repo/Repo.hs b/testdata/debian/Debian/Repo/Repo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Repo.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Repo
+    ( Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , repoArchList
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    ) where
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Set (Set, unions)
+import Data.Text (unpack)
+import Debian.Arch (Arch)
+import Debian.Repo.EnvPath (EnvPath(..))
+import Debian.Repo.Release (Release(releaseArchitectures))
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
+
+repoArchList :: Repo r => r -> Set Arch
+repoArchList = unions . map releaseArchitectures . repoReleaseInfo
diff --git a/testdata/debian/Debian/Repo/Slice.hs b/testdata/debian/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Slice.hs
@@ -0,0 +1,149 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, PackageImports, StandaloneDeriving, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    , sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , UpdateError(..)
+    , SourcesChangedAction(..)
+    , doSourcesChangedAction
+    ) where
+
+import Control.Exception (Exception)
+import Data.Data (Data)
+import Data.List (intersperse)
+import Data.Typeable (Typeable)
+import Debian.Pretty (PP(..), ppDisplay, ppPrint)
+import Debian.Release (ReleaseName(relName))
+import Debian.Repo.Prelude (replaceFile)
+import Debian.Repo.Prelude.Verbosity (ePutStr, ePutStrLn)
+import Debian.Repo.Repo (RepoKey)
+import Debian.Sources (DebSource(..), SliceName, SourceType(..))
+import System.Directory (createDirectoryIfMissing, removeFile)
+import System.IO (hGetLine, stdin)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), hcat, text)
+
+data Slice = Slice {sliceRepoKey :: RepoKey, sliceSource :: DebSource} deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty (PP SliceList) where
+    pPrint = hcat . intersperse (text "\n") . map (ppPrint . sliceSource) . slices . unPP
+
+instance Pretty (PP NamedSliceList) where
+    pPrint = ppPrint . sliceList . unPP
+
+instance Pretty (PP ReleaseName) where
+    pPrint = ppPrint . relName . unPP
+
+deriving instance Show SourceType
+deriving instance Show DebSource
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+{-
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+-}
+
+data UpdateError
+    = Changed ReleaseName FilePath SliceList SliceList
+    | Missing ReleaseName FilePath
+    | Flushed
+    deriving Typeable
+
+instance Exception UpdateError
+
+instance Show UpdateError where
+    show (Changed r p l1 l2) = unwords ["Changed", show r, show p, ppDisplay l1, ppDisplay l2]
+    show (Missing r p) = unwords ["Missing", show r, show p]
+    show Flushed = "Flushed"
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+doSourcesChangedAction :: FilePath -> FilePath -> NamedSliceList -> SliceList -> SourcesChangedAction -> IO ()
+doSourcesChangedAction dir sources baseSources fileSources SourcesChangedError = do
+  ePutStrLn ("The sources.list in the existing '" ++ (relName . sliceListName $ baseSources) ++ "' in " ++ dir ++
+             " apt-get environment doesn't match the parameters passed to the autobuilder" ++ ":\n\n" ++
+             sources ++ ":\n\n" ++
+             ppDisplay fileSources ++
+	     "\nRun-time parameters:\n\n" ++
+             ppDisplay baseSources ++ "\n" ++
+	     "It is likely that the build environment in\n" ++
+             dir ++ " is invalid and should be rebuilt.")
+  ePutStr $ "Remove it and continue (or exit)?  [y/n]: "
+  result <- hGetLine stdin
+  case result of
+    ('y' : _) ->
+        do removeRecursiveSafely dir
+           createDirectoryIfMissing True dir
+           replaceFile sources (ppDisplay baseSources)
+    _ -> error ("Please remove " ++ dir ++ " and restart.")
+
+doSourcesChangedAction dir sources baseSources _fileSources RemoveRelease = do
+  ePutStrLn $ "Removing suspect environment: " ++ dir
+  removeRecursiveSafely dir
+  createDirectoryIfMissing True dir
+  replaceFile sources (ppDisplay baseSources)
+
+doSourcesChangedAction dir sources baseSources _fileSources UpdateSources = do
+  -- The sources.list has changed, but it should be
+  -- safe to update it.
+  ePutStrLn $ "Updating environment with new sources.list: " ++ dir
+  removeFile sources
+  replaceFile sources (ppDisplay baseSources)
diff --git a/testdata/debian/Debian/Repo/SourceTree.hs b/testdata/debian/Debian/Repo/SourceTree.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/SourceTree.hs
@@ -0,0 +1,314 @@
+{-# LANGUAGE CPP, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, PackageImports, ScopedTypeVariables #-}
+module Debian.Repo.SourceTree
+    ( addLogEntry
+    , buildDebs
+    , HasTopDir(topdir)
+    , HasSubDir(subdir)
+    , HasDebDir(debdir)
+    , HasChangeLog(entry)
+    , HasSourceTree(findSourceTree, copySourceTree)
+    , HasBuildTree(findBuildTree)
+    , explainSourcePackageStatus
+    , findChanges
+    , findDebianBuildTrees
+    , findDebianSourceTrees
+    , findOneDebianBuildTree
+    , findOrigTarball
+    , origTarballPath
+    , BuildDecision(..)
+    , SourcePackageStatus (Indep, All, None)
+    , DebianBuildTree (debTree', topdir')
+    , DebianSourceTree(tree', control')
+    , SourceTree(dir')
+    ) where
+
+import Control.Applicative ((<$>), (<*>), pure)
+import Control.Exception (evaluate, SomeException, try, throw)
+import Control.Monad (foldM)
+import Control.Monad.Trans (MonadIO(..))
+import Data.List (intercalate, nubBy, sortBy)
+import Data.Time (NominalDiffTime)
+import Debian.Changes (ChangeLogEntry(..), ChangesFile(..), parseEntries)
+import Debian.Control.Policy (HasDebianControl(debianControl), DebianControl, parseDebianControlFromFile)
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Changes (findChangesFiles)
+import Debian.Repo.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.MonadOS (MonadOS(getOS))
+import Debian.Repo.OSImage (osRoot)
+import Debian.Repo.Prelude (rsync, getSubDirectories, replaceFile, dropPrefix)
+import Debian.Repo.Prelude.Verbosity (readProcFailing, timeTask, noisier, modifyProcessEnv)
+import qualified Debian.Version as V (version)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
+import System.Environment (getEnv, getEnvironment)
+import System.Exit (ExitCode(ExitFailure), ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetContents, IOMode(ReadMode), withFile)
+import System.Process (CmdSpec(..), CreateProcess(cwd, env, cmdspec), proc, readProcessWithExitCode, showCommandForUser)
+import System.Process.Chunks (collectProcessTriple)
+import System.Unix.Chroot (useEnv)
+
+class HasTopDir t where
+    topdir :: t -> FilePath
+
+class HasSubDir t where
+    subdir :: t -> FilePath
+
+class HasDebDir t where
+    debdir :: t -> FilePath
+
+class HasChangeLog t where
+    entry :: t -> ChangeLogEntry
+
+class HasSourceTree t where
+    findSourceTree :: FilePath -> IO t
+    -- ^ This just determines whether path is a directory and if so
+    -- wraps SourceTree around it.
+    copySourceTree :: t -> FilePath -> IO t
+
+class HasBuildTree t where
+    findBuildTree :: FilePath -> String -> IO t
+    -- ^ This applies findSourceTree to path </> name.
+
+-- |Find the .changes file which is generated by a successful run of
+-- dpkg-buildpackage.
+findChanges :: DebianBuildTree -> IO ChangesFile
+findChanges tree =
+    do let dir = topdir tree
+       result <- findChangesFiles dir
+       case result of
+         [cf] -> return cf
+         [] -> fail ("Couldn't find .changes file in " ++ dir)
+         lst -> fail ("Multiple .changes files in " ++ dir ++ ": " ++ intercalate ", " (map ppDisplay lst))
+
+-- |Rewrite the changelog with an added entry.
+addLogEntry :: (HasChangeLog t, HasDebDir t) => ChangeLogEntry -> t -> IO ()
+addLogEntry entry'' debtree =
+-- readFile changelogPath >>= replaceFile changelogPath . ((show (pretty entry'')) ++)
+  withFile changelogPath ReadMode (\ handle -> hGetContents handle >>= replaceFile changelogPath . ((ppDisplay entry'' ++ "\n\n") ++))
+    where
+      changelogPath = (debdir debtree) ++ "/debian/changelog"
+
+-- |There are three possible results of a build: an upload consisting
+-- of only the architecture independent debs (Indep), one including
+-- both indep and binary debs (All), or with a failed build (None).
+data SourcePackageStatus = All | Indep [BinPkgName] | None deriving (Show, Eq)
+
+explainSourcePackageStatus :: SourcePackageStatus -> String
+explainSourcePackageStatus All = "All architecture dependent files for the current build architecture are present."
+explainSourcePackageStatus (Indep missing) = "Some or all architecture-dependent files for the current build architecture are missing: " ++ show missing
+explainSourcePackageStatus None = "This version of the package is not present."
+
+-- |Represents a decision whether to build a package, with a text juststification.
+data BuildDecision
+    = Yes String
+    | No String
+    | Arch String	-- Needs a -B build, architecture dependent files only
+    | Auto String	-- Needs a 'automated' rebuild, with a generated version number and log entry
+    | Error String	-- A fatal condition was encountered - e.g. a build dependency became older since last build
+
+instance Show BuildDecision where
+    show (Yes reason) = "Yes - " ++ reason
+    show (No reason) = "No - " ++ reason
+    show (Arch reason) = "Yes - " ++ reason
+    show (Auto reason) = "Yes - " ++ reason
+    show (Error reason) = "Error - " ++ reason
+
+-- | Run dpkg-buildpackage in a build tree.
+buildDebs :: (MonadOS m, MonadIO m) => Bool -> Bool -> [(String, Maybe String)] -> DebianBuildTree -> BuildDecision -> m NominalDiffTime
+buildDebs noClean _twice setEnv buildTree decision =
+    do
+      root <- rootPath . osRoot <$> getOS
+      noSecretKey <- liftIO $ getEnv "HOME" >>= return . (++ "/.gnupg") >>= doesDirectoryExist >>= return . not
+      env0 <- liftIO getEnvironment
+      -- Set LOGNAME so dpkg-buildpackage doesn't die when it fails to
+      -- get the original user's login information
+      let run cmd =
+              liftIO $ do
+                cmd' <- modifyProcessEnv (("LOGNAME", Just "root") : setEnv) cmd
+                let cmd'' = cmd' {cwd = dropPrefix root path}
+                timeTask $ useEnv root forceList $ readProcFailing cmd'' ""
+      _ <- liftIO $ run (proc "chmod" ["ugo+x", "debian/rules"])
+      let buildCmd = proc "dpkg-buildpackage" (concat [["-sa"],
+                                                       case decision of Arch _ -> ["-B"]; _ -> [],
+                                                       if noSecretKey then ["-us", "-uc"] else [],
+                                                       if noClean then ["-nc"] else []])
+      (result, elapsed) <- liftIO . noisier 4 $ run buildCmd
+      case collectProcessTriple result of
+        (ExitFailure n, _, _) -> fail $ "*** FAILURE: " ++ showCmd (cmdspec buildCmd) ++ " -> " ++ show n
+        _ -> return elapsed
+    where
+      path = debdir buildTree
+      showCmd (RawCommand cmd args) = showCommandForUser cmd args
+      showCmd (ShellCommand cmd) = cmd
+
+forceList :: [a] -> IO [a]
+forceList output = evaluate (length output) >> return output
+
+findOrigTarball :: DebianBuildTree -> IO (Maybe FilePath)
+findOrigTarball tree =
+    do exists <- doesFileExist (origTarballPath tree)
+       return $ if exists then Just (origTarballPath tree) else Nothing
+
+origTarballPath :: DebianBuildTree -> FilePath
+origTarballPath tree =
+    topdir tree ++ "/" ++ orig
+    where
+      orig = name ++ "_" ++ version ++ ".orig.tar.gz"
+      name = logPackage . entry $ tree
+      version = V.version . logVersion . entry $ tree
+
+-- deprecated
+{-
+copyDebianSourceTree :: (DebianSourceTreeC t) => t -> FilePath -> IO t
+copyDebianSourceTree = copySourceTree
+
+copyDebianBuildTree :: (DebianBuildTreeC t) => t -> FilePath -> IO t
+copyDebianBuildTree = copySourceTree
+-}
+
+-- |Find all the debian source trees in a directory.
+findDebianSourceTrees :: FilePath -> IO [(FilePath, DebianSourceTree)]
+findDebianSourceTrees path =
+    getSubDirectories path >>= \ (subdirs :: [FilePath]) ->
+    foldM (\ pairs subdir'' ->
+               try (findSourceTree (path </> subdir'')) >>=
+               either (\ (_ :: SomeException) -> return pairs) (\ tree -> return ((subdir'', tree) : pairs))) [] subdirs
+
+-- |Find all the debian source trees in a directory.
+findDebianBuildTrees :: FilePath -> IO [DebianBuildTree]
+findDebianBuildTrees path =
+    getSubDirectories path >>=
+    foldM (\ trees subdir'' ->
+               try (findBuildTree path subdir'') >>=
+               either (\ (_ :: SomeException) -> return trees) (\ tree -> return $ tree : trees)) []
+
+-- |Find a DebianBuildTree inside a directory.  It finds all the
+-- DebianSourceTrees, and if they all have the same package name it
+-- returns the newest one according to the version numbers.  If there
+-- are none, or there are trees with different package names, Nothing
+-- is returned.
+findOneDebianBuildTree :: FilePath -> IO (Maybe DebianBuildTree)
+findOneDebianBuildTree path =
+    do trees <- findDebianBuildTrees path
+       -- Do all the trees have the same package name?
+       case nubBy eqNames trees of
+         -- Yes, return the newest one
+         [_] -> return . Just . head . sortBy cmpVers $ trees
+         -- No trees found
+         [] -> return Nothing
+         -- No, throw an exception
+         names -> error $ "findOneDebianBuildTree: more than one source package name found in " ++ path ++ ": " ++ show (map (logPackage . entry) names)
+    where
+      eqNames tree1 tree2 = (logPackage . entry $ tree1) == (logPackage . entry $ tree2)
+      cmpVers tree1 tree2 = compare (logVersion . entry $ tree1) (logVersion . entry $ tree2)
+
+-- |Any directory containing source code.
+data SourceTree =
+    SourceTree {dir' :: FilePath} deriving Show
+
+-- |A Debian source tree, which has a debian subdirectory containing
+-- at least a control file and a changelog.
+data DebianSourceTree =
+    DebianSourceTree {tree' :: SourceTree,
+                      control' :: DebianControl,
+                      entry' :: ChangeLogEntry}
+    deriving Show
+
+instance HasDebianControl DebianSourceTree where
+    debianControl = control'
+
+-- |A Debian source tree plus a parent directory, which is where the
+-- binary and source deb packages appear after a build.  Note that
+-- topdir' </> subdir' == dir' . tree' . debTree'
+data DebianBuildTree =
+    DebianBuildTree {topdir' :: FilePath,
+                     subdir' :: String,
+                     debTree' :: DebianSourceTree}
+    deriving Show
+
+instance HasTopDir DebianSourceTree where
+    topdir = dir' . tree'
+
+instance HasDebDir DebianSourceTree where
+    debdir = dir' . tree'
+
+instance HasChangeLog DebianSourceTree where
+    entry = entry'
+
+instance HasDebDir DebianBuildTree where
+    debdir t = topdir' t </> subdir' t
+
+instance HasTopDir DebianBuildTree where
+    topdir = topdir'
+
+instance HasSubDir DebianBuildTree where
+    subdir = subdir'
+
+instance HasTopDir SourceTree where
+    topdir = dir'
+
+instance HasSourceTree SourceTree where
+    findSourceTree path =
+        doesDirectoryExist path >>= \ exists ->
+        case exists of
+          False -> fail $ "No such directory: " ++ path
+          True -> return $ SourceTree path
+    copySourceTree tree dest =
+        createDirectoryIfMissing True dest >>
+        rsync [] (topdir tree) dest >>
+        return (SourceTree dest)
+
+instance HasSourceTree DebianSourceTree where
+    findSourceTree path0 =
+      findSourceTree path0 >>= \ (tree :: SourceTree) ->
+      parseDebianControlFromFile (path0 ++ "/debian/control") >>= either throw return >>= \ c ->
+      -- We only read part of the changelog, so be careful that the file
+      -- descriptor gets closed.
+      withFile (path0 ++ "/debian/changelog") ReadMode $ \ handle ->
+          hGetContents handle >>= \ l ->
+          case parseEntries l of
+            (Right e : _) ->
+              -- ePutStrLn ("findDebianSourceTree " ++ show path0 ++ " -> " ++ topdir tree) >>
+              return (DebianSourceTree tree c e)
+            (Left msgs : _) -> error $ "Bad changelog entry in " ++ show (path0 ++ "/debian/changelog") ++ ": " ++ intercalate ", " msgs
+            [] -> return $ error $ "Empty changelog file: " ++ show (path0 ++ "/debian/changelog")
+    copySourceTree tree dest =
+        DebianSourceTree <$> copySourceTree (tree' tree) dest
+                         <*> pure (control' tree)
+                         <*> pure (entry' tree)
+
+instance HasSourceTree DebianBuildTree where
+    findSourceTree path =
+        do trees <- findDebianBuildTrees path
+           case nubBy eqNames trees of
+             [_] -> return . head . sortBy cmpVers $ trees
+             [] -> error $ "No source trees found in subdirectorys of " ++ path
+             names -> error $ "Mutiple source package names found in " ++ path ++ ": " ++ show (map (logPackage . entry) names)
+        where
+          eqNames tree1 tree2 = (logPackage . entry $ tree1) == (logPackage . entry $ tree2)
+          cmpVers tree1 tree2 = compare (logVersion . entry $ tree1) (logVersion . entry $ tree2)
+    copySourceTree build dest =
+        copySource >>= copyTarball >>= return . moveBuild
+        where
+          copySource = createDirectoryIfMissing True dest >> rsync [] (topdir' build) dest
+          -- copySource = DebianBuildTree <$> pure dest <*> pure (subdir' tree) <*> copySourceTree (debTree' tree) (dest </> subdir' tree)
+          copyTarball (ExitFailure _, _, _) = error $ "Failed to copy source tree: " ++ topdir' build ++ " -> " ++ dest
+          copyTarball (ExitSuccess, _, _) =
+              do exists <- liftIO $ doesFileExist origPath
+                 case exists of
+                   False -> return (ExitSuccess, "", "")
+                   True -> liftIO $ readProcessWithExitCode "cp" ["-p", origPath, dest ++ "/"] ""
+          moveBuild (ExitFailure _, _, _) = error $ "Failed to copy Tarball: " ++ origPath ++ " -> " ++ dest ++ "/"
+          moveBuild (ExitSuccess, _, _) = build {topdir' = dest, debTree' = moveSource (debTree' build)}
+          moveSource source = source {tree' = SourceTree {dir' = dest </> subdir build}}
+          origPath = topdir build </> orig
+          orig = name ++ "_" ++ version ++ ".orig.tar.gz"
+          name = logPackage . entry $ build
+          version = V.version . logVersion . entry $ build
+
+instance HasBuildTree DebianBuildTree where
+    findBuildTree path d = findSourceTree (path </> d) >>= return . DebianBuildTree path d
+
+instance HasChangeLog DebianBuildTree where
+    entry = entry' . debTree'
diff --git a/testdata/debian/Debian/Repo/State/AptImage.hs b/testdata/debian/Debian/Repo/State/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/AptImage.hs
@@ -0,0 +1,167 @@
+{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, TemplateHaskell #-}
+{-# OPTIONS -Wall -fno-warn-orphans #-}
+module Debian.Repo.State.AptImage
+    ( withAptImage
+    , aptSourcePackages
+    , aptBinaryPackages
+    , prepareSource
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Exception (SomeException)
+import Control.Monad.Catch (MonadCatch, catch)
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (MonadIO(..), MonadTrans(lift))
+import Data.List (sort)
+import Data.Maybe (listToMaybe)
+import Debian.Changes (ChangeLogEntry(logVersion))
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (SrcPkgName(unSrcPkgName))
+import Debian.Release (ReleaseName)
+import Debian.Repo.AptImage (aptDir, aptGetSource, aptGetUpdate)
+import Debian.Repo.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.Internal.Apt (AptImage(aptImageArch, aptImageRoot, aptImageSources,
+                                          aptBinaryPackageCache, aptSourcePackageCache),
+                                 cacheRootDir, createAptImage, MonadApt(..), modifyApt)
+import Debian.Repo.Internal.Repos (AptKey, evalMonadApt, getAptKey, MonadRepos(..), putAptImage)
+import Debian.Repo.PackageID (PackageID(packageName), PackageID(packageVersion))
+import Debian.Repo.PackageIndex (BinaryPackage, SourcePackage(sourcePackageID))
+import Debian.Repo.Prelude (symbol)
+import Debian.Repo.Prelude.Verbosity (qPutStr, qPutStrLn)
+import Debian.Repo.Slice (NamedSliceList(sliceList, sliceListName), SliceList, SourcesChangedAction)
+import Debian.Repo.SourceTree (DebianBuildTree(debTree'), DebianSourceTree(tree'), HasChangeLog(entry), findDebianBuildTrees, SourceTree(dir'))
+import Debian.Repo.State.PackageIndex (binaryPackagesFromSources, sourcePackagesFromSources)
+import Debian.Repo.State.Slice (updateCacheSources)
+import Debian.Repo.Top (MonadTop)
+import Debian.Version (DebianVersion)
+import System.Directory (createDirectoryIfMissing)
+import System.Unix.Directory (removeRecursiveSafely)
+
+instance MonadApt m => MonadApt (StateT EnvRoot m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadRepos m => MonadRepos (StateT AptImage m) where
+    getRepos = lift getRepos
+    putRepos = lift . putRepos
+
+withAptImage :: (MonadRepos m, MonadTop m) => SourcesChangedAction -> NamedSliceList -> StateT AptImage m a -> m a
+withAptImage sourcesChangedAction sources action = prepareAptImage sourcesChangedAction sources >>= evalMonadApt action
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+prepareAptImage :: forall m. (MonadTop m, MonadRepos m) =>
+                 SourcesChangedAction	-- What to do if environment already exists and sources.list is different
+              -> NamedSliceList		-- The sources.list
+              -> m AptKey		-- The resulting environment
+prepareAptImage sourcesChangedAction sources = do
+  mkey <- getAptKey =<< cacheRootDir (sliceListName sources)
+  maybe (prepareAptImage' sourcesChangedAction sources) return mkey
+
+prepareAptImage' :: forall m. (MonadCatch m, MonadTop m, MonadRepos m) => SourcesChangedAction -> NamedSliceList -> m AptKey
+prepareAptImage' sourcesChangedAction sources = do
+  mkey <- getAptKey =<< cacheRootDir (sliceListName sources)
+  maybe (prepareAptImage'' `catch` handle) return mkey
+    where
+      handle :: SomeException -> m AptKey
+      handle e = do
+        qPutStrLn ("Exception preparing " ++ (ppDisplay . sliceListName $ sources) ++ ": " ++ show e)
+        removeAptImage
+        prepareAptImage''
+      prepareAptImage'' = do
+        qPutStrLn ($(symbol 'prepareAptImage) ++ ": " ++ (ppDisplay . sliceListName $ sources))
+        key <- putAptImage =<< createAptImage sources
+        evalMonadApt (updateCacheSources sourcesChangedAction sources >> aptGetUpdate) key
+        return key
+      removeAptImage = cacheRootDir (sliceListName sources) >>= liftIO . removeRecursiveSafely . rootPath
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+-- updateAptEnv :: (MonadRepos m, MonadApt m) => m ()
+-- updateAptEnv = aptGetUpdate
+
+aptSourcePackages :: (MonadRepos m, MonadApt m) => m [SourcePackage]
+aptSourcePackages = do
+  mpkgs <- aptSourcePackageCache <$> getApt
+  maybe aptSourcePackages' return mpkgs
+    where
+      aptSourcePackages' = do
+        root <- aptImageRoot <$> getApt
+        arch <- aptImageArch <$> getApt
+        sources <- aptImageSources <$> getApt
+        -- qPutStrLn ($(symbol 'aptSourcePackages) ++ " " ++ show (pretty (sliceListName sources)))
+        pkgs <- sourcePackagesFromSources root arch (sliceList sources)
+        modifyApt (\ s -> s {aptSourcePackageCache = Just pkgs})
+        return pkgs
+
+aptBinaryPackages :: (MonadRepos m, MonadApt m) => m [BinaryPackage]
+aptBinaryPackages = do
+  mpkgs <- aptBinaryPackageCache <$> getApt
+  maybe aptBinaryPackages' return mpkgs
+    where
+      aptBinaryPackages' = do
+        root <- aptImageRoot <$> getApt
+        arch <- aptImageArch <$> getApt
+        sources <- aptImageSources <$> getApt
+        -- qPutStrLn ($(symbol 'aptBinaryPackages) ++ " " ++ show (pretty (sliceListName sources)))
+        pkgs <- binaryPackagesFromSources root arch (sliceList sources)
+        modifyApt (\ s -> s {aptBinaryPackageCache = Just pkgs})
+        return pkgs
+{-
+    do qPutStrLn "AptImage.getBinaryPackages"
+       root <- getL aptImageRoot <$> getApt
+       arch <- getL aptImageArch <$> getApt
+       sources <- (sliceList . getL aptImageSources) <$> getApt
+       binaryPackagesFromSources root arch sources
+-}
+
+data UpdateError
+    = Changed ReleaseName FilePath SliceList SliceList
+    | Missing ReleaseName FilePath
+    | Flushed
+
+instance Show UpdateError where
+    show (Changed r p l1 l2) = unwords ["Changed", show r, show p, ppDisplay l1, ppDisplay l2]
+    show (Missing r p) = unwords ["Missing", show r, show p]
+    show Flushed = "Flushed"
+
+-- |Retrieve a source package via apt-get.
+prepareSource :: (MonadRepos m, MonadApt m, MonadTop m, MonadIO m) =>
+                 SrcPkgName			-- The name of the package
+              -> Maybe DebianVersion		-- The desired version, if Nothing get newest
+              -> m DebianBuildTree		-- The resulting source tree
+prepareSource package version =
+    do root <- (rootPath . aptImageRoot) <$> getApt
+       dir <- aptDir package
+       liftIO $ createDirectoryIfMissing True dir
+       ready <- liftIO $ findDebianBuildTrees dir
+       version' <- latestVersion package version
+       case (version', ready) of
+         (Nothing, _) ->
+             fail $ "No available versions of " ++ unSrcPkgName package ++ " in " ++ root
+         (Just requested, [tree])
+             | requested == (logVersion . entry $ tree) ->
+                 return tree
+         (Just requested, []) ->
+             do aptGetSource dir [(package, Just requested)]
+                trees <- liftIO $ findDebianBuildTrees dir
+                case trees of
+                  [tree] -> return tree
+                  _ -> fail $ "apt-get source failed in " ++ dir ++ " (1): trees=" ++ show (map (dir' . tree' . debTree') trees)
+         (Just requested, _) ->
+             do -- One or more incorrect versions are available, remove them
+                liftIO $ removeRecursiveSafely dir
+                qPutStr $ "Retrieving APT source for " ++ unSrcPkgName package
+                aptGetSource dir [(package, Just requested)]
+                trees <- liftIO $ findDebianBuildTrees dir
+                case trees of
+                  [tree] -> return tree
+                  _ -> fail $ "apt-get source failed (2): trees=" ++ show (map (dir' . tree' . debTree') trees)
+
+-- | Find the most recent version of a source package.
+latestVersion :: (MonadRepos m, MonadApt m) => SrcPkgName -> Maybe DebianVersion -> m (Maybe DebianVersion)
+latestVersion package exact = do
+  pkgs <- aptSourcePackages
+  let versions = map (packageVersion . sourcePackageID) $ filter ((== package) . packageName . sourcePackageID) $ pkgs
+      newest = listToMaybe $ reverse $ sort $ versions
+  return $ maybe newest Just exact
diff --git a/testdata/debian/Debian/Repo/State/OSImage.hs b/testdata/debian/Debian/Repo/State/OSImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/OSImage.hs
@@ -0,0 +1,305 @@
+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, PackageImports, ScopedTypeVariables #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Debian.Repo.State.OSImage
+    ( buildArchOfOS
+    , osBinaryPackages
+    , osSourcePackages
+    , prepareOS
+    , updateOS
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.DeepSeq (force)
+import Control.Exception (SomeException, throw)
+import Control.Monad (when)
+import Control.Monad.Catch (catch, try, MonadMask)
+import Control.Monad.Trans (liftIO, MonadIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..))
+import qualified Debian.Debianize.Types.Atoms as EnvSet (EnvSet(cleanOS, dependOS))
+import Debian.Pretty (ppDisplay)
+import Debian.Relation (BinPkgName(BinPkgName))
+import Debian.Release (ReleaseName(relName))
+import Debian.Repo.EnvPath (EnvRoot(EnvRoot, rootPath))
+import Debian.Repo.Internal.IO (buildArchOfRoot)
+import Debian.Repo.Internal.Repos (osFromRoot, MonadRepos, putOSImage)
+import Debian.Repo.LocalRepository (LocalRepository)
+import Debian.Repo.OSImage (createOSImage, OSImage(osArch, osBaseDistro, osLocalMaster,
+                                                   osRoot, osSourcePackageCache, osBinaryPackageCache),
+                            pbuilder, debootstrap, localeGen, neuterEnv, osFullDistro)
+import Debian.Repo.MonadOS (MonadOS(getOS, modifyOS), evalMonadOS, aptGetInstall, syncLocalPool, syncOS)
+import Debian.Repo.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Prelude (replaceFile)
+import Debian.Repo.Prelude.SSH (sshCopy)
+import Debian.Repo.Prelude.Verbosity (ePutStrLn, quieter, qPutStrLn)
+import Debian.Repo.Slice (NamedSliceList(sliceListName), Slice(sliceSource), SliceList(slices), SourcesChangedAction(SourcesChangedError), UpdateError(..))
+import Debian.Repo.State.PackageIndex (binaryPackagesFromSources, sourcePackagesFromSources)
+import Debian.Repo.State.Slice (verifySourcesList)
+import Debian.Repo.Top (askTop, distDir, MonadTop, sourcesPath)
+import Debian.Sources (DebSource(sourceUri), parseSourcesList)
+import Debian.URI (URI(uriScheme))
+import System.Directory (createDirectoryIfMissing, doesFileExist)
+import System.Environment (getEnv)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>), splitFileName)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Chunks (collectProcessTriple)
+import Debian.Repo.Prelude.Verbosity (readProcFailing)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+
+buildArchOfOS :: (MonadIO m, MonadOS m) => m Arch
+buildArchOfOS = do
+  root <- rootPath . osRoot <$> getOS
+  liftIO $ do
+    setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+    a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+    b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+    case (code1, lines out1, code2, lines out2) of
+      (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+          return $ Binary (ArchOS os) (ArchCPU cpu)
+      _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+
+osSourcePackages :: (MonadRepos m, MonadOS m) => m [SourcePackage]
+osSourcePackages = do
+  mpkgs <- osSourcePackageCache <$> getOS
+  maybe osSourcePackages' return mpkgs
+    where
+      osSourcePackages' = do
+        root <- osRoot <$> getOS
+        arch <- osArch <$> getOS
+        dist <- osFullDistro <$> getOS
+        pkgs <- sourcePackagesFromSources root arch dist
+        qPutStrLn ("Read " ++ show (length pkgs) ++ " release source packages")
+        modifyOS (\ s -> s {osSourcePackageCache = Just pkgs})
+        return pkgs
+
+osBinaryPackages :: (MonadRepos m, MonadOS m) => m [BinaryPackage]
+osBinaryPackages = do
+  mpkgs <- osBinaryPackageCache <$> getOS
+  maybe osBinaryPackages' return mpkgs
+    where
+      osBinaryPackages' = do
+        root <- osRoot <$> getOS
+        arch <- osArch <$> getOS
+        dist <- osFullDistro <$> getOS
+        pkgs <- binaryPackagesFromSources root arch dist
+        qPutStrLn ("Read " ++ show (length pkgs) ++ " release binary packages")
+        modifyOS (\ s -> s {osBinaryPackageCache = Just pkgs})
+        return pkgs
+
+-- |Find or create and update an OS image.  Returns paths to clean and
+-- depend os images.
+prepareOS
+    :: (MonadRepos m, MonadTop m, MonadMask m, MonadIO m) =>
+       EnvSet.EnvSet		-- ^ The location where image is to be built
+    -> NamedSliceList		-- ^ The sources.list of the base distribution
+    -> LocalRepository           -- ^ The location of the local upload repository
+    -> Bool			-- ^ If true, remove and rebuild the image
+    -> Bool			-- ^ If true, flush all the build dependencies
+    -> SourcesChangedAction	-- ^ What to do if called with a sources.list that
+				-- differs from the previous call
+    -> [String]			-- ^ Extra packages to install - e.g. keyrings
+    -> [String]			-- ^ More packages to install, but these may not be available
+                                -- immediately - e.g seereason-keyring.  Ignore exceptions.
+    -> [String]			-- ^ Packages to exclude
+    -> [String]			-- ^ Components of the base repository
+    -> m (EnvRoot, EnvRoot)
+prepareOS eset distro repo flushRoot flushDepends ifSourcesChanged include optional exclude components =
+    do cleanOS <- osFromRoot cleanRoot >>= maybe (do os <- liftIO (createOSImage cleanRoot distro repo)
+                                                     putOSImage os
+                                                     return os) return
+       if flushRoot then evalMonadOS (recreate Flushed) cleanRoot else (qPutStrLn ("Updating " ++ show cleanRoot) >>
+                                                                        evalMonadOS updateOS cleanRoot `catch` (\ (e :: UpdateError) -> evalMonadOS (recreate e) cleanRoot))
+       evalMonadOS (doInclude >> doLocales) cleanRoot
+       when flushDepends (ePutStrLn "sync clean -> depend" >> evalMonadOS (syncOS dependRoot) cleanRoot)
+       -- Try running a command in the depend environment, if it fails
+       -- sync dependOS from cleanOS.
+       dependOS <- osFromRoot dependRoot
+       case dependOS of
+         Nothing -> do (arch :: Either SomeException Arch) <- (liftIO $ try $ useEnv (rootPath dependRoot) return buildArchOfRoot)
+                       case arch of
+                         Left _ -> evalMonadOS (syncOS dependRoot) cleanRoot
+                         Right _ ->
+                             do -- ePutStrLn "createOSImage dependRoot?  I don't understand why this would be done."
+                                os <- liftIO (createOSImage dependRoot distro repo)
+                                putOSImage os
+         Just _ -> return ()
+       evalMonadOS syncLocalPool dependRoot
+       return (cleanRoot, dependRoot)
+    where
+      cleanRoot = EnvRoot (EnvSet.cleanOS eset)
+      dependRoot = EnvRoot (EnvSet.dependOS eset)
+      recreate :: (MonadOS m, MonadTop m, MonadMask m, MonadRepos m, MonadIO m) => UpdateError -> m ()
+      recreate (Changed name path computed installed)
+          | ifSourcesChanged == SourcesChangedError =
+              error $ "FATAL: Sources for " ++ relName name ++ " in " ++ path ++
+                       " don't match computed configuration.\n\ncomputed:\n" ++
+                       ppDisplay computed ++ "\ninstalled:\n" ++
+                       ppDisplay installed
+      recreate reason =
+          do let root = EnvSet.cleanOS eset
+             base <- osBaseDistro <$> getOS
+             sources <- sourcesPath (sliceListName base)
+             dist <- distDir (sliceListName base)
+             liftIO $ do ePutStrLn $ "Removing and recreating build environment at " ++ root ++ ": " ++ show reason
+                         -- ePutStrLn ("removeRecursiveSafely " ++ rootPath root)
+                         removeRecursiveSafely root
+                         -- ePutStrLn ("createDirectoryIfMissing True " ++ show dist)
+                         createDirectoryIfMissing True dist
+                         -- ePutStrLn ("writeFile " ++ show sources ++ " " ++ show (show . osBaseDistro $ os))
+                         replaceFile sources (ppDisplay base)
+             rebuildOS (EnvRoot root) distro include exclude components
+
+      doInclude :: (MonadOS m, MonadIO m, MonadMask m) => m ()
+      doInclude =
+          do aptGetInstall (map (\ s -> (BinPkgName s, Nothing)) include)
+             aptGetInstall (map (\ s -> (BinPkgName s, Nothing)) optional) `catch` (\ (e :: IOError) -> ePutStrLn ("Ignoring exception on optional package install: " ++ show e))
+      doLocales :: (MonadOS m, MonadIO m) => m ()
+      doLocales =
+          do os <- getOS
+             localeName <- liftIO $ try (getEnv "LANG")
+             liftIO $ localeGen os (either (\ (_ :: IOError) -> "en_US.UTF-8") id localeName)
+
+-- | Not used, but could be a substitute for buildOS.
+_pbuilderBuild :: (MonadRepos m, MonadTop m, MonadMask m) =>
+            EnvRoot
+         -> NamedSliceList
+         -> LocalRepository
+         -> [String]
+         -> [String]
+         -> [String]
+         -> m OSImage
+_pbuilderBuild root distro repo _extraEssential _omitEssential _extra =
+    do top <- askTop
+       os <- liftIO $ pbuilder top root distro repo _extraEssential _omitEssential _extra
+       putOSImage os
+       try (evalMonadOS updateOS root) >>= either (\ (e :: SomeException) -> error (show e)) return
+       return os
+
+rebuildOS :: (MonadOS m, MonadRepos m, MonadTop m, MonadMask m) =>
+             EnvRoot			-- ^ The location where image is to be built
+           -> NamedSliceList		-- ^ The sources.list of the base distribution
+           -> [String]			-- ^ Extra packages to install - e.g. keyrings
+           -> [String]			-- ^ Packages to exclude
+           -> [String]			-- ^ Components of the base repository
+           -> m ()
+rebuildOS root distro include exclude components =
+          do master <- osLocalMaster <$> getOS
+             _key <- buildOS root distro master include exclude components
+             syncLocalPool
+
+-- | Create a new clean build environment in root.clean FIXME: create
+-- an ".incomplete" flag and remove it when build-env succeeds
+buildOS :: (MonadRepos m, MonadTop m, MonadMask m) =>
+            EnvRoot
+         -> NamedSliceList
+         -> LocalRepository
+         -> [String]
+         -> [String]
+         -> [String]
+         -> m OSImage
+buildOS root distro repo include exclude components =
+    do os <- liftIO $ debootstrap root distro repo include exclude components
+       putOSImage os
+       evalMonadOS updateOS root
+       liftIO $ neuterEnv os
+       return os
+
+-- | Try to update an existing build environment: run apt-get update
+-- and dist-upgrade.
+updateOS :: (MonadOS m, MonadRepos m, MonadMask m) => m ()
+updateOS = quieter 1 $ do
+  root <- (rootPath . osRoot) <$> getOS
+  liftIO $ createDirectoryIfMissing True (root </> "etc")
+  liftIO $ readFile "/etc/resolv.conf" >>= writeFile (root </> "etc/resolv.conf")
+  liftIO $ prepareDevs root
+  syncLocalPool
+  verifySources
+  -- Disable the starting of services in the changeroot
+  _ <- liftIO $ useEnv root (return . force) $ readProcessWithExitCode "dpkg-divert" ["--local", "--rename", "--add", "/sbin/initctl"] ""
+  _ <- liftIO $ useEnv root (return . force) $ readProcessWithExitCode "ln" ["-s", "/bin/true", "/sbin/initctl"] ""
+  code <- liftIO $ sshCopy root
+  case code of
+    ExitSuccess -> return ()
+    _ -> error $ "sshCopy -> " ++ show code
+    where
+      verifySources :: (MonadOS m, MonadRepos m) => m ()
+      verifySources =
+          do root <- osRoot <$> getOS
+             computed <- remoteOnly <$> osFullDistro <$> getOS
+             let sourcesPath' = rootPath root </> "etc/apt/sources.list"
+             text <- liftIO (try $ readFile sourcesPath')
+             installed <-
+                 case text of
+                   Left (_ :: SomeException) -> return Nothing
+                   Right s -> verifySourcesList (Just root) (parseSourcesList s) >>= return . Just . remoteOnly
+             case installed of
+               Nothing -> (osBaseDistro <$> getOS) >>= \ sources -> throw $ Missing (sliceListName sources) sourcesPath'
+               Just installed'
+                   | installed' /= computed ->
+                       (osBaseDistro <$> getOS) >>= \ sources -> throw $ Changed (sliceListName sources) sourcesPath' computed installed'
+               _ -> return ()
+      remoteOnly :: SliceList -> SliceList
+      remoteOnly x = x {slices = filter r (slices x)} where r y = (uriScheme . sourceUri . sliceSource $ y) /= "file:"
+{-
+    get >>= updateOS' >>= either throw put
+    where
+      updateOS' :: MonadRepos m => OSImage -> m (Either UpdateError OSImage)
+      updateOS' os =
+          do let root = getL osRoot os
+             liftIO $ createDirectoryIfMissing True (rootPath root ++ "/etc")
+             liftIO $ readFile "/etc/resolv.conf" >>= writeFile (rootPath root ++ "/etc/resolv.conf")
+             verified <- verifySources os
+             case verified of
+               Left x -> return $ Left x
+               Right _ ->
+                   do liftIO $ prepareDevs (rootPath root)
+                      evalMonadOS (syncLocalPool >> updateLists) os
+                      _ <- liftIO $ sshCopy (rootPath root)
+                      source' <- evalMonadOS getSourcePackages' os'
+                      binary <- evalMonadOS getBinaryPackages' os'
+                      return . Right $ setL osSourcePackages source' $ setL osBinaryPackages binary $ os'
+      verifySources :: MonadRepos m => OSImage -> m (Either UpdateError OSImage)
+      verifySources os =
+          do let root = getL osRoot os
+             computed <- remoteOnly <$> evalMonadOS osFullDistro os
+             let sourcesPath' = rootPath root ++ "/etc/apt/sources.list"
+             text <- liftIO (try $ readFile sourcesPath')
+             installed <-
+                 case text of
+                   Left (_ :: SomeException) -> return Nothing
+                   Right s -> verifySourcesList (Just root) (parseSourcesList s) >>= return . Just . remoteOnly
+             case installed of
+               Nothing -> return $ Left $ Missing (sliceListName (getL osBaseDistro os)) sourcesPath'
+               Just installed'
+                   | installed' /= computed ->
+                       return $ Left $ Changed (sliceListName (getL osBaseDistro os)) sourcesPath' computed installed'
+               _ -> return $ Right os
+      remoteOnly :: SliceList -> SliceList
+      remoteOnly x = x {slices = filter r (slices x)} where r y = (uriScheme . sourceUri . sliceSource $ y) /= "file:"
+-}
+
+-- |Prepare a minimal \/dev directory
+{-# WARNING prepareDevs "This function should check all the result codes" #-}
+prepareDevs :: FilePath -> IO ()
+prepareDevs root = do
+  mapM_ prepareDev devices
+  where
+    devices :: [(FilePath, String, Int, Int)]
+    devices = [(root ++ "/dev/null", "c", 1, 3),
+               (root ++ "/dev/zero", "c", 1, 5),
+               (root ++ "/dev/full", "c", 1, 7),
+               (root ++ "/dev/console", "c", 5, 1),
+               (root ++ "/dev/random", "c", 1, 8),
+               (root ++ "/dev/urandom", "c", 1, 9)] ++
+              (map (\ n -> (root ++ "/dev/loop" ++ show n, "b", 7, n)) [0..7]) ++
+              (map (\ n -> (root ++ "/dev/loop/" ++ show n, "b", 7, n)) [0..7])
+    prepareDev (path, typ, major, minor) = do
+                     createDirectoryIfMissing True (fst (splitFileName path))
+                     let cmd = "mknod " ++ path ++ " " ++ typ ++ " " ++ show major ++ " " ++ show minor ++ " 2> /dev/null"
+                     exists <- doesFileExist path
+                     case exists of
+                       False -> readProcFailing (shell cmd) L.empty >>= return . collectProcessTriple >>= \ (result, _, _) -> return result
+                       True -> return ExitSuccess
diff --git a/testdata/debian/Debian/Repo/State/Package.hs b/testdata/debian/Debian/Repo/State/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/Package.hs
@@ -0,0 +1,1163 @@
+-- | Install packages to and delete packages from a local repository.
+{-# LANGUAGE BangPatterns, FlexibleContexts, FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, TemplateHaskell, TupleSections, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-name-shadowing #-}
+module Debian.Repo.State.Package
+    ( MonadInstall
+    , evalInstall
+    , scanIncoming
+    , installPackages
+    , deleteTrumped
+    , deleteBinaryOrphans
+    , deleteGarbage
+    , deleteSourcePackages
+    -- * Install result
+    , InstallResult(..)
+    , resultToProblems
+    , explainError
+    , explainErrors
+    , showErrors
+    , releaseKey
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Exception (SomeException)
+import Control.Exception as E (ErrorCall(ErrorCall), SomeException(..), try)
+import Control.Monad (filterM, foldM, when)
+import Control.Monad.State (StateT, runStateT, MonadState(get, put))
+import Control.Monad.Trans (liftIO, MonadIO, lift)
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString, fromChunks, readFile)
+import Data.Digest.Pure.MD5 (md5)
+import Data.Either (partitionEithers, lefts, rights)
+import Data.Lens.Lazy (getL, modL)
+import Data.Lens.Template (makeLenses)
+import Data.List as List (filter, groupBy, intercalate, intersperse, isSuffixOf, map, partition, sortBy)
+import Data.Map as Map (fromList, lookup)
+import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
+import Data.Monoid ((<>), mconcat)
+import Data.Set as Set (difference, empty, fold, fromList, insert, map, member, null, Set, size, toAscList, toList, union, unions, singleton)
+import Data.Text as T (pack, Text, unpack)
+import qualified Data.Text as T (concat)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Changes (ChangedFileSpec(..), ChangesFile(..), changesFileName)
+import Debian.Control (ControlFunctions(stripWS), formatControl, formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (appendFields, Control, Control'(Control), ControlFunctions(lookupP), ControlFunctions(parseControlFromHandle), Field, Field'(Field), fieldValue, modifyField, Paragraph, raiseFields, renameField)
+import qualified Debian.Control.Text as S (Control'(Control), ControlFunctions(parseControlFromFile))
+import Debian.Pretty (PP(..), ppPrint, ppDisplay)
+import Debian.Relation (BinPkgName, PkgName)
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (parseSection', ReleaseName, releaseName', Section(..), sectionName, sectionName', SubSection(section))
+import Debian.Repo.Changes (changeName, changePath, findChangesFiles)
+import Debian.Repo.EnvPath (EnvPath, outsidePath)
+import Debian.Repo.Internal.Repos (MonadRepos(..), ReleaseKey, releaseByKey, putRelease)
+import Debian.Repo.LocalRepository (Layout(..), LocalRepository, poolDir', repoLayout, repoReleaseInfoLocal, repoRoot)
+import Debian.Repo.PackageID (makeBinaryPackageID, makeSourcePackageID, PackageID(packageName, packageVersion), prettyPackageID)
+import Debian.Repo.PackageIndex (binaryIndexes, BinaryPackage(packageID, packageInfo), BinaryPackage(BinaryPackage, pConflicts, pDepends, pPreDepends, pProvides, pReplaces), PackageIndex(..), packageIndexes, packageIndexPath, SourceControl(..), SourceFileSpec(SourceFileSpec, sourceFileName), sourceIndexes, SourcePackage(sourcePackageID), SourcePackage(SourcePackage, sourceControl, sourceDirectory, sourcePackageFiles, sourceParagraph))
+import Debian.Repo.Prelude (nub')
+import Debian.Repo.Prelude.Files (writeAndZipFileWithBackup)
+import Debian.Repo.Prelude.Misc (listDiff)
+import Debian.Repo.Prelude.GPGSign (PGPKey)
+import Debian.Repo.Prelude.Verbosity (ePutStrLn, qPutStr, qPutStrLn)
+import Debian.Repo.Repo (Repo, repoArchList, repoKey, repoKeyURI)
+import Debian.Repo.Release (Release(releaseAliases, releaseComponents, releaseName, releaseArchitectures))
+import Debian.Repo.State.Release (findReleases, prepareRelease, writeRelease, signRepo)
+import Debian.URI (fileFromURIStrict)
+import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)
+import Debian.Version.Text ()
+import Network.URI (URI(uriPath))
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getDirectoryContents, removeFile, renameFile)
+import System.Exit (ExitCode(..))
+import System.FilePath ((</>), splitFileName)
+import System.IO ()
+import qualified System.Posix.Files as F (createLink, fileSize, getFileStatus)
+import System.Posix.Types (FileOffset)
+import System.Process (runInteractiveCommand, waitForProcess)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), text, hcat)
+
+-- | A monad for installing or deleting a repository's packages
+class MonadRepos m => MonadInstall m where
+    getInstall :: m InstallState
+    putInstall :: InstallState -> m ()
+
+data InstallState
+    = InstallState
+      { _repository :: LocalRepository
+      , _live :: Maybe (Set Text)
+      -- ^ All the files that are part of this repository - debs,
+      -- original tarballs, index files, etc.
+      , _releases :: [Release]
+      -- ^ All the releases in this repository.
+      , _modified :: Set ReleaseKey
+      -- ^ Releases which have been modified and need to be rewritten and signed
+      }
+
+instance MonadRepos m => MonadInstall (StateT InstallState m) where
+    getInstall = get
+    putInstall = put
+
+instance MonadRepos m => MonadRepos (StateT InstallState m) where
+    getRepos = lift getRepos
+    putRepos = lift . putRepos
+
+$(makeLenses [''InstallState])
+
+runInstall :: MonadRepos m => StateT InstallState m a -> LocalRepository -> Maybe PGPKey -> m (a, InstallState)
+runInstall task repo keyname = do
+  releases <- findReleases repo
+  (r, st) <- runStateT task (InstallState repo Nothing releases empty)
+  mapM_ (\ key -> releaseByKey key >>= \ rel -> liftIO (writeRelease repo rel >>= signRepo keyname repo)) (Set.toList (getL modified st))
+  return (r, st)
+
+evalInstall :: MonadRepos m => StateT InstallState m a -> LocalRepository -> Maybe PGPKey -> m a
+evalInstall task repo keyname = fst <$> runInstall task repo keyname
+
+modifyInstall :: MonadInstall m => (InstallState -> InstallState) -> m ()
+modifyInstall f = getInstall >>= putInstall . f
+
+data InstallResult
+    = Ok
+    | Failed [Problem]		-- Package can not currently be installed
+    | Rejected [Problem]	-- Package can not ever be installed
+    deriving (Show, Eq)
+
+data Problem
+    = NoSuchRelease ReleaseName
+    | NoSuchSection ReleaseName [Section]
+    | ShortFile FilePath FileOffset FileOffset
+    -- ^ A file in the incoming directory is shorter than its entry
+    -- in the .changes file indicates - maybe it is still uploading?
+    | LongFile FilePath FileOffset FileOffset
+    -- ^ A file in the incoming directory is longer than its entry
+    -- in the .changes file indicates.  Maybe it is left over from a
+    -- previous failed upload, or a concurrent upload from somewhere
+    -- else.  (That should not be permitted to happen, the .changes
+    -- file should be a lock for uploads of that package/version.)
+    | MissingFromIncoming FilePath
+    -- ^ A file listed in the .changes file is missing from incoming.
+    | MissingFromPool FilePath
+    -- ^ A file listed in an index file is not in the pool
+    | BadChecksum FilePath String String
+    -- ^ A file which is listed in the .changes file exists in incoming
+    -- and is the right length but its checksum is wrong.
+    | InvalidControlInfo FilePath String
+    -- ^ Badly formatted control file
+    | DuplicatePackage Duplicate
+    -- ^ A package in incoming has the same ID as a package already in the index
+    | OtherProblem String
+    deriving (Eq)
+
+data Duplicate
+    = Duplicate {release :: Release, index :: PackageIndex, newPackage :: BinaryPackage, oldPackage :: BinaryPackage} deriving (Eq, Show)
+
+instance Show Problem where
+    show (NoSuchRelease rel) = "NoSuchRelease  " ++ releaseName' rel
+    show (NoSuchSection rel sect) = "NoSuchSection " ++ releaseName' rel ++ " " ++ show (List.map sectionName' sect)
+    show (ShortFile path a b) = "ShortFile " ++ path ++ " " ++ show a ++ " " ++ show b
+    show (LongFile path a b) = "LongFile " ++ path ++ " " ++ show a ++ " " ++ show b
+    show (MissingFromIncoming path) = "MissingFromIncoming " ++ path
+    show (MissingFromPool path) = "MissingFromPool " ++ path
+    show (BadChecksum path a b) = "BadChecksum " ++ path ++ " " ++ show a ++ " " ++ show b
+    show (DuplicatePackage (Duplicate rel idx old _new)) = "DuplicatePackage in " ++ show rel ++ ":" ++ show idx ++ ": " ++ show (packageID old)
+    show (InvalidControlInfo path a) = "InvalidControlInfo " ++ path ++ " " ++ show a
+    show (OtherProblem s) = "OtherProblem " ++ show s
+
+releaseKey :: MonadInstall m => Release -> m ReleaseKey
+releaseKey release = do
+  repo <- getL repository <$> getInstall
+  putRelease repo release
+
+mergeResults :: [InstallResult] -> InstallResult
+mergeResults results =
+    doMerge Ok results
+    where
+      doMerge x [] = x
+      doMerge x (Ok : more) = doMerge x more
+      doMerge (Rejected p1) (Rejected p2 : more) = doMerge (Rejected (p1 ++ p2)) more
+      doMerge (Rejected p1) (Failed p2 : more) = doMerge (Rejected (p1 ++ p2)) more
+      doMerge (Failed p1) (Rejected p2 : more) = doMerge (Rejected (p1 ++ p2)) more
+      doMerge (Failed p1) (Failed p2 : more) = doMerge (Failed (p1 ++ p2)) more
+      doMerge Ok (x : more) = doMerge x more
+
+showErrors :: [InstallResult] -> String
+showErrors = intercalate "\n" . List.map explainError . concatMap resultToProblems
+
+-- | Return the list of issues that provoked a result - the Ok result
+-- becomes the empty list, a Failed or Rejected result becomes a
+-- non-empty list.
+resultToProblems :: InstallResult -> [Problem]
+resultToProblems Ok = []
+resultToProblems (Failed x) = x
+resultToProblems (Rejected x) = x
+
+explainErrors :: [InstallResult] -> [String]
+explainErrors = List.map explainError . concatMap resultToProblems
+
+explainError :: Problem -> String
+explainError (NoSuchRelease dist) =
+    ("\nThe distribution in the .changes file (" ++ releaseName' dist ++ ") does not exist.  There\n" ++
+     "are three common reasons this might happen:\n" ++
+     " (1) The value in the latest debian/changelog entry is wrong\n" ++
+     " (2) A new release needs to be created in the repository.\n" ++
+     "       newdist --root <root> --create-release " ++ releaseName' dist ++ "\n" ++
+     " (3) A new alias needs to be created in the repository (typically 'unstable', 'testing', or 'stable'.)\n" ++
+     "       newdist --root <root> --create-alias <existing release> " ++ releaseName' dist ++ "\n")
+explainError (NoSuchSection dist components) =
+    ("\nThe component" ++ plural "s" components ++ " " ++ intercalate ", " (List.map sectionName' components) ++
+     " in release " ++ releaseName' dist ++ " " ++
+     plural "do" components ++ " not exist.\n" ++
+     "either the 'Section' value in debian/control was wrong or the section needs to be created:" ++
+     concat (List.map (\ component -> "\n  newdist --root <root> --create-section " ++ releaseName' dist ++ "," ++ sectionName' component) components))
+explainError (ShortFile path a b) =
+    ("\nThe file " ++ path ++ "\n" ++
+     "is shorter than it should be (expected: " ++ show a ++ ", actual: " ++ show b ++ ".)  This usually\n" ++
+     "happens while the package is still being uploaded to the repository.")
+explainError (LongFile path _ _) =
+    ("\nThe file " ++ path ++
+     "\nis longer than it should be.  This can happen when the --force-build\n" ++
+     "option is used.  In this case the --flush-pool option should help.")
+explainError (BadChecksum path _ _) =
+    ("\nThe checksum of the file " ++ path ++ "\n" ++
+     "is different from the value in the .changes file.\n" ++
+     "This can happen when the --force-build option is used.  In this case the\n" ++
+     "--flush-pool option should help.  It may also mean a hardware failure.")
+explainError other = show other
+
+plural :: String -> [a] -> String
+plural "do" [_] = "does"
+plural "do" _ = "do"
+
+plural "s" [_] = ""
+plural "s" _ = "s"
+
+plural _ _ = ""
+
+-- | Find all the .changes files in the incoming directory and try to
+-- process each to install the package into a local repository.
+scanIncoming :: (MonadInstall m, MonadRepos m) => Bool -> Maybe PGPKey -> LocalRepository -> m [(ChangesFile, InstallResult)]
+scanIncoming createSections keyname repo = do
+  qPutStrLn ("Uploading packages to " ++ outsidePath (repoRoot repo) </> "incoming")
+  changes <- liftIO (findChangesFiles (outsidePath (repoRoot repo) </> "incoming"))
+  case changes of
+    [] -> qPutStrLn "Nothing to install."
+    _ -> qPutStrLn ("To install:\n  " ++ (intercalate "\n  " . List.map ppDisplay $ changes))
+  results <- installPackages createSections keyname repo changes
+  case results of
+    [] -> return ()
+    _ -> qPutStrLn ("Upload results:\n  " ++ (intercalate "\n  " . List.map (uncurry showResult) $ (zip changes results)))
+  return (zip changes results)
+    where
+      showResult changes result =
+          changesFileName changes ++ ": " ++
+          case result of
+            Ok -> "Ok"
+            Failed lst -> "Failed -\n      " ++ (intercalate "\n      " $ List.map show lst)
+            Rejected lst -> "Rejected -\n      " ++ (intercalate "\n      " $ List.map show lst)
+
+-- | Install several packages into a repository.  This means
+-- 1. getting the list of files from the .changes file,
+-- 2. verifying the file checksums,
+-- 3. deleting any existing version and perhaps other versions which
+--    were listed in the delete list,
+-- 4. updating the Packages and Sources files, and
+-- 5. moving the files from the incoming directory to the proper
+--    place in the package pool.
+installPackages :: MonadRepos m =>
+                   Bool			-- ^ ok to create new releases and sections
+                -> Maybe PGPKey		-- ^ key to sign repository with
+                -> LocalRepository	-- ^ destination repository
+                -> [ChangesFile]	-- ^ Packages to be installed
+                -> m [InstallResult]	-- ^ Outcome of each source package
+installPackages createSections keyname repo changeFileList =
+    evalInstall
+      (do results' <- foldM (installFiles createSections) [] changeFileList
+          results'' <- updateIndexes (reverse results')
+          when (elem Ok results'')
+               (do mapM_ (uncurry (finish (repoRoot repo) (maybe Flat id (repoLayout repo)))) (zip changeFileList results'')
+                   let releaseNames = nub' (List.map changeRelease changeFileList)
+                   releases' <- catMaybes <$> mapM findRelease' releaseNames
+                   mapM_ (\ rel -> liftIO $ writeRelease repo rel >>= signRepo keyname repo) releases')
+          return results'')
+      repo
+      keyname
+    where
+      -- Update all the index files affected by the successful
+      -- installs.  This is a time consuming operation, so we want to
+      -- do this all at once, rather than one package at a time
+      updateIndexes :: MonadInstall m => [InstallResult] -> m [InstallResult]
+      updateIndexes results =
+          do (pairLists :: [Either InstallResult [((Release, PackageIndex), B.Paragraph)]]) <-
+                 mapM (uncurry buildInfo) (zip changeFileList results)
+             let sortedByIndex = sortBy compareIndex (concat (rights pairLists))
+             let groupedByIndex = undistribute (groupBy (\ a b -> compareIndex a b == EQ) sortedByIndex)
+             result <- addPackagesToIndexes groupedByIndex
+             case result of
+               Ok -> return $ List.map (either id (const Ok)) pairLists
+               problem -> return $ List.map (const problem) results
+          where
+            compareIndex :: ((Release, PackageIndex), B.Paragraph) -> ((Release, PackageIndex), B.Paragraph) -> Ordering
+            compareIndex (a, _) (b, _) = compare a b
+
+-- Build the control information to be added to the package indexes.
+buildInfo :: MonadInstall m => ChangesFile -> InstallResult -> m (Either InstallResult [((Release, PackageIndex), B.Paragraph)])
+buildInfo changes Ok =
+          do mrel <- findRelease' (changeRelease changes)
+             case mrel of
+               Just release ->
+                   do (info :: [Either InstallResult B.Paragraph]) <- mapM (fileInfo changes) indexFiles
+                      case lefts info of
+                        [] ->
+                            let (pairs :: [(Set (Release, PackageIndex), Either InstallResult B.Paragraph)]) = zip (indexLists release) info in
+                            let (pairs' :: [(Set (Release, PackageIndex), B.Paragraph)]) =
+                                    catMaybes $ List.map (\ (a, b) -> either (const Nothing) (\ b' -> Just (a, b')) b) pairs in
+                            let (pairs'' :: [((Release, PackageIndex), B.Paragraph)]) = concat (List.map distribute pairs') in
+                            return (Right pairs'')
+                        results -> return (Left (mergeResults results))
+               Nothing -> return . Left . Failed $ [NoSuchRelease (changeRelease changes)]
+          where
+            indexLists :: Release -> [Set (Release, PackageIndex)]
+            indexLists release = List.map (indexes release) indexFiles
+            indexes :: Release -> ChangedFileSpec -> Set (Release, PackageIndex)
+            indexes release file = Set.map (\ arch -> (release, PackageIndex (section . changedFileSection $ file) arch)) (archSet release changes file)
+            indexFiles = dsc ++ debs
+            (debs :: [ChangedFileSpec]) = filter f files
+                where (f :: ChangedFileSpec -> Bool) = (isSuffixOf ".deb" . changedFileName)
+                      (files :: [ChangedFileSpec]) = (changeFiles changes)
+            dsc = filter (isSuffixOf ".dsc" . changedFileName) (changeFiles changes)
+            -- (debs, nonDebs) = partition (isSuffixOf ".deb" . changedFileName) (changeFiles changes)
+            -- (indepDebs, archDebs) = partition (isSuffixOf "_all.deb" . changedFileName) debs
+            -- (dsc, other) = partition (isSuffixOf ".dsc" . changedFileName) nonDebs
+            --fileIndex release file = List.map (PackageIndex release (section . changedFileSection $ file)) (archList release changes file)
+buildInfo _ notOk = return . Left $ notOk
+
+-- For a successful install this unlinks the files from INCOMING and
+-- moves the .changes file into INSTALLED.  For a failure it moves
+-- all the files to REJECT.
+finish :: MonadInstall m => EnvPath -> Layout -> ChangesFile -> InstallResult -> m ()
+finish root layout changes Ok =
+          do --vPutStrBl 1 stderr $ "  finish Ok " ++ changesFileName changes
+             mapM_ (liftIO . removeFile . ((outsidePath root </> "incoming") </>) . changedFileName) (changeFiles changes)
+             installChangesFile root layout changes
+finish root _ changes (Rejected _) =
+          do --vPutStrBl 1 stderr $ "  finish Rejected " ++ changesFileName changes
+             mapM_ (\ name -> liftIO $ moveFile (outsidePath root </> "incoming" </> name) (outsidePath root </> "reject" </> name))
+                      (List.map changedFileName (changeFiles changes))
+             liftIO $ moveFile (outsidePath root </> "incoming" </> changeName changes) (outsidePath root </> "reject" </> changeName changes)
+finish _ _ changes (Failed _) =
+          do qPutStrLn $ "  Finish Failed " ++ changesFileName changes
+             return ()
+
+installChangesFile :: MonadInstall m => EnvPath -> Layout -> ChangesFile -> m ()
+installChangesFile root layout changes =
+          liftIO (moveFile (changePath changes) dst)
+          where dst = case layout of
+                        Flat -> outsidePath root </> changeName changes
+                        Pool -> outsidePath root </> "installed" </> changeName changes
+
+incoming :: MonadInstall m => m FilePath
+incoming = do
+  root <- repoRoot . getL repository <$> getInstall
+  return $ outsidePath root </> "incoming" {- </> changedFileName file -}
+
+reject :: MonadInstall m => ChangedFileSpec -> m FilePath
+reject file = do
+  root <- repoRoot . getL repository <$> getInstall
+  return $ outsidePath root </> "reject" </> changedFileName file
+
+-- | Get information about one of the (.deb or .dsc) files listed in a .changes file.
+fileInfo :: MonadInstall m => ChangesFile -> ChangedFileSpec -> m (Either InstallResult B.Paragraph)
+fileInfo changes file = do
+  repo <- getL repository <$> getInstall
+  getControl >>= addFields repo
+                where
+                  getControl :: MonadInstall m => m (Either InstallResult B.Paragraph)
+                  getControl =
+                      do -- path <- incoming file
+                         dir <- incoming
+                         control <-
+                             case isSuffixOf ".deb" . changedFileName $ file of
+                               -- Extract the control information from a .deb
+                               True -> getDebControl
+                               -- This is the .dsc file, parse it
+                               False -> liftIO $ S.parseControlFromFile (dir </> changedFileName file) >>= return . either (Left . show) Right
+                         case control of
+                           -- The control file should contain one paragraph
+                           Right (S.Control [info]) -> return (Right info)
+                           Left message -> return . Left . Rejected $ [InvalidControlInfo (dir </> changedFileName file) message]
+                           Right (S.Control _) -> return . Left . Rejected $ [InvalidControlInfo (dir </> changedFileName file) "Expected one paragraph"]
+                  addFields :: MonadInstall m => LocalRepository -> (Either InstallResult B.Paragraph) -> m (Either InstallResult B.Paragraph)
+                  addFields _ (Left result) = return $ Left result
+                  addFields repo (Right info) =
+                      case isSuffixOf ".deb" . changedFileName $ file of
+                        True -> addDebFields repo changes file info
+                        False -> addSourceFields repo changes file info
+                  -- | Extract the control file from a binary .deb.
+                  getDebControl :: MonadInstall m => m (Either String B.Control)
+                  getDebControl =
+                      do dir <- incoming
+                         let cmd = "ar p " ++ dir </> changedFileName file ++ " control.tar.gz | tar xzO ./control"
+                         (_, outh, _, handle) <- liftIO $ runInteractiveCommand cmd
+                         control <- liftIO $ B.parseControlFromHandle cmd outh >>= return . either (Left . show) Right
+                         exitcode <- liftIO $ waitForProcess handle
+                         case exitcode of
+                           ExitSuccess -> return control
+                           ExitFailure n -> return . Left $ "Failure: " ++ cmd ++ " -> " ++ show n
+
+findRelease' :: MonadInstall m => ReleaseName -> m (Maybe Release)
+findRelease' name = do
+    rels <- getL releases <$> getInstall
+    return $ findRelease rels name
+
+findRelease :: [Release] -> ReleaseName -> Maybe Release
+findRelease releases name =
+    case filter (\ release -> elem name (releaseName release : releaseAliases release)) releases of
+      [] -> Nothing
+      [x] -> Just x
+      _ -> error $ "Internal error 16 - multiple releases named " ++ releaseName' name
+
+markReleaseModified :: MonadInstall m => ReleaseKey -> m ()
+markReleaseModified = modifyInstall . modL modified . Set.insert
+
+-- | Hard link the files of each package into the repository pool,
+-- but don't unlink the files in incoming in case of subsequent
+-- failure.
+installFiles :: MonadInstall m => Bool -> [InstallResult] -> ChangesFile -> m [InstallResult]
+installFiles createSections results changes = do
+  mrel <- findOrCreateRelease (changeRelease changes)
+  maybe (return (Failed [NoSuchRelease (changeRelease changes)] : results)) (installFiles' createSections changes results) mrel
+    where
+      findOrCreateRelease :: MonadInstall m => ReleaseName -> m (Maybe Release)
+      findOrCreateRelease name = do
+        rels <- getL releases <$> getInstall
+        case createSections of
+            False -> return (findRelease rels name)
+            True -> do let release = findRelease rels name
+                       repo <- (getL repository <$> getInstall)
+                       case release of
+                         Nothing ->
+                             do newRelease <- prepareRelease repo name [] [parseSection' "main"] (repoArchList repo)
+                                modifyInstall (modL releases (newRelease :))
+                                return (Just newRelease)
+                         Just release -> return (Just release)
+
+installFiles' :: MonadInstall m => Bool -> ChangesFile -> [InstallResult] -> Release -> m [InstallResult]
+installFiles' createSections changes results release =
+                let sections = nub' . List.map (section . changedFileSection) . changeFiles $ changes in
+                case (createSections, listDiff sections (releaseComponents release)) of
+                  (_, []) -> installFiles'' changes results
+                  (True, missing) ->
+                      do qPutStrLn ("Creating missing sections: " ++ intercalate " " (List.map sectionName' missing))
+                         repo <- getL repository <$> getInstall
+                         release' <- prepareRelease repo (releaseName release) [] missing (releaseArchitectures release)
+                         installFiles'' changes results
+                  (False, missing) ->
+                      return (Failed [NoSuchSection (releaseName release) missing] : results)
+
+installFiles'' :: MonadInstall m => ChangesFile -> [InstallResult] -> m [InstallResult]
+installFiles'' changes results = do
+  repo <- getL repository <$> getInstall
+  result <- mapM (installFile changes) (changeFiles changes) >>= return . mergeResults
+  when (result == Ok) (modifyInstall (modL live (Just . Set.union (paths repo) . fromMaybe empty)))
+  return $ result : results
+    where
+      paths repo = Set.fromList $ List.map (T.pack . ((outsidePath (repoRoot repo)) </>) . poolDir' repo changes) (changeFiles changes)
+
+-- | Move one file into the repository
+installFile :: MonadInstall m => ChangesFile -> ChangedFileSpec -> m InstallResult
+installFile changes file = do
+  repo <- getL repository <$> getInstall
+  live' <- getL live <$> getInstall
+  let root = repoRoot repo
+  let dir = outsidePath root </> poolDir' repo changes file
+  let src = outsidePath root </> "incoming" </> changedFileName file
+  let dst = dir </> changedFileName file
+  installed <- liftIO $ doesFileExist dst
+  available <- liftIO $ doesFileExist src
+  let indexed = maybe False (Set.member (T.pack dst)) live' -- Is the file already part of the repository?
+  case (available, indexed, installed) of
+    (False, _, _) -> do                        -- Perhaps this file is about to be uploaded
+      return (Failed [MissingFromIncoming src])
+    (True, False, False) -> do         -- This just needs to be installed
+      liftIO (createDirectoryIfMissing True dir)
+      liftIO (F.createLink src dst)
+      return Ok
+    (True, False, True) -> do          -- A garbage file is already present
+      qPutStrLn ("  Replacing unlisted file: " ++ dst)
+      liftIO (removeFile dst)
+      liftIO (F.createLink src dst)
+      return Ok
+    (True, True, False) -> do
+      -- The repository is damaged - the file is listed in the index
+      -- but it does not exist at that location.
+      return (Failed [MissingFromPool dst])
+    (True, True, True) -> do           -- Further inspection is required
+      installedSize <- liftIO $ F.getFileStatus dst >>= return . F.fileSize
+      installedMD5sum <- liftIO $ L.readFile dst >>= return . show . md5
+      let status =
+              case (compare (changedFileSize file) installedSize, compare (changedFileMD5sum file) installedMD5sum) of
+                -- Somehow the correct file is already installed - so be it.
+                (EQ, EQ) -> Ok
+                -- The wrong file of the right length is installed
+                (EQ, _) -> Rejected [BadChecksum dst (changedFileMD5sum file) installedMD5sum]
+                -- File may be in the process of being uploaded
+                (LT, _) -> Failed [ShortFile dst (changedFileSize file) installedSize]
+                -- This must be the wrong file
+                (GT, _) -> Rejected [LongFile dst (changedFileSize file) installedSize]
+      return status
+
+archSet :: Release -> ChangesFile -> ChangedFileSpec -> Set Arch
+archSet release changes file =
+    case () of
+      _ | isSuffixOf "_all.deb" name -> releaseArchitectures release
+      _ | isSuffixOf ".deb" name -> singleton (changeArch changes)
+      _ | isSuffixOf ".udeb" name -> empty
+      _ -> singleton Source
+    where name = changedFileName file
+
+distribute :: (Set a, b) -> [(a, b)]
+distribute (iset, p) = List.map (\ i -> (i, p)) (toList iset)
+
+undistribute :: [[(a, b)]] -> [(a, [b])]
+-- undistribute pairss = Map.toList (Map.fromListWith (++) (map (\ (a, b) -> (a, [b])) (concat pairss)))
+undistribute [] = []
+undistribute ([] : tail) = undistribute tail
+undistribute (((index, info) : items) : tail) =
+    (index, info : List.map snd items) : undistribute tail
+
+addDebFields :: MonadInstall m => LocalRepository -> ChangesFile -> ChangedFileSpec -> Paragraph' Text -> m (Either InstallResult (Paragraph' Text))
+addDebFields repo changes file info =
+    let (binaryVersion :: DebianVersion) =
+            maybe (error $ "Missing 'Version' field") parseDebianVersion (B.fieldValue "Version" info) in
+    let (newfields :: [B.Field]) =
+            [B.Field (T.pack "Source", " " <> source <> T.pack (versionSuffix binaryVersion)),
+             B.Field (T.pack "Filename", T.pack (" " ++ poolDir' repo changes file </> changedFileName file)),
+             B.Field (T.pack "Size", T.pack (" " ++ show (changedFileSize file))),
+             B.Field (T.pack "MD5sum", T.pack (" " ++ changedFileMD5sum file))] in
+    return $ Right $ B.appendFields newfields info
+    where
+      versionSuffix :: DebianVersion -> String
+      versionSuffix binaryVersion = if binaryVersion /= sourceVersion then " (" ++ show (prettyDebianVersion sourceVersion) ++ ")" else ""
+      source = maybe (error "Missing 'Source' field in .changes file") id (B.fieldValue "Source" (changeInfo changes))
+      sourceVersion = changeVersion changes
+
+
+addSourceFields :: MonadInstall m => LocalRepository -> ChangesFile -> ChangedFileSpec -> B.Paragraph -> m (Either InstallResult B.Paragraph)
+addSourceFields repo changes file info =
+    return . Right . append . raise . modify . rename $ info
+    where
+      rename = B.renameField (T.pack "Source") (T.pack "Package")
+      modify = B.modifyField (T.pack "Files") (\ b -> (T.pack (T.unpack b ++ "\n " ++ filesLine file))) .
+               B.modifyField (T.pack "Checksums-Sha1") (\ b -> (T.pack (T.unpack b ++ "\n " ++ sha1Line file))) .
+               B.modifyField (T.pack "Checksums-Sha256") (\ b -> (T.pack (T.unpack b ++ "\n " ++ sha256Line file)))
+      raise = B.raiseFields (== (T.pack "Package"))
+      append = B.appendFields $
+               [B.Field (T.pack "Priority", T.pack (" " ++ changedFilePriority file)),
+                B.Field (T.pack "Section", T.pack  (" " ++ (sectionName (changedFileSection file)))),
+                B.Field (T.pack "Directory", T.pack (" " ++ poolDir' repo changes file))] ++
+               maybe [] (\ s -> [B.Field (T.pack "Build-Info", " " <> s)]) (B.fieldValue "Build-Info" (changeInfo changes))
+      filesLine file = changedFileMD5sum file ++ " "  ++ show (changedFileSize file) ++ " " ++ changedFileName file
+      sha1Line file = changedFileSHA1sum file ++ " "  ++ show (changedFileSize file) ++ " " ++ changedFileName file
+      sha256Line file = changedFileSHA256sum file ++ " "  ++ show (changedFileSize file) ++ " " ++ changedFileName file
+{-
+    let info' = B.renameField (T.pack "Source") (T.pack "Package") info in
+    let info'' = B.modifyField (T.pack "Files") (\ b -> (T.pack (B.unpack b ++ "\n " ++ changedFileMD5sum file ++ " "  ++
+                                                                 show (changedFileSize file) ++ " " ++
+                                                                 changedFileName file))) info' in
+    let info''' = B.raiseFields (== (T.pack "Package")) info'' in
+    let newfields = [B.Field (T.pack "Priority", T.pack (" " ++ changedFilePriority file)),
+                     B.Field (T.pack "Section", T.pack  (" " ++ (sectionName (changedFileSection file)))),
+                     B.Field (T.pack "Directory", T.pack (" " ++ poolDir' release changes file))] ++
+                    maybe [] (\ s -> [B.Field (T.pack "Build-Info", T.pack (" " ++ s))])
+                              (B.fieldValue "Build-Info" (changeInfo changes)) in
+    Right $ B.appendFields newfields info'''
+-}
+
+moveFile :: FilePath -> FilePath -> IO ()
+moveFile src dst =
+    do --vPutStrBl 1 stderr ("moveFile " ++ src ++ " " ++ dst)
+       doesFileExist dst >>= (flip when) (removeFile dst)
+       F.createLink src dst
+       removeFile src
+
+-- |Add control information to several package indexes, making sure
+-- that that no duplicate package ids are inserted.
+addPackagesToIndexes :: MonadInstall m => [((Release, PackageIndex), [B.Paragraph])] -> m InstallResult
+addPackagesToIndexes pairs =
+    do repo <- getL repository <$> getInstall
+       oldPackageLists <- mapM (uncurry getPackages_) indexKeys
+       case partitionEithers oldPackageLists of
+         -- No errors
+         ([], oldPackageLists') ->
+             do -- if none of the new packages are already in the index, add them
+                let newPackageLists = List.map (\ ((release, index), info) -> List.map (toBinaryPackage_ release index) info) pairs
+                    dupes :: [Duplicate]
+                    dupes = concat (List.map (\ ((rel, idx), old, new) -> findDupes rel idx old new) (zip3 indexKeys oldPackageLists' newPackageLists))
+                case dupes of
+                  [] -> do mapM_ (updateIndex repo) (zip3 indexKeys oldPackageLists' newPackageLists)
+                           return Ok
+                  dupes -> return $ Failed (List.map DuplicatePackage dupes)
+         (bad, _) -> return $ Failed (List.map (OtherProblem . show) bad)
+    where
+      findDupes :: Release -> PackageIndex -> [BinaryPackage] -> [BinaryPackage] -> [Duplicate]
+      findDupes rel idx oldList newList =
+          -- Assuming here that each package in oldList has a unique packageID
+          let oldMap = Map.fromList (zip (List.map packageID oldList) oldList) in
+          mapMaybe (\ new -> fmap (Duplicate rel idx new) (Map.lookup (packageID new) oldMap)) newList
+          -- (\ (oldList, newList) -> filter (\ new -> any (== (packageID new)) (List.map packageID oldList)) newList)
+      updateIndex repo ((release, index), oldPackages, newPackages) = liftIO $ putPackages_ repo release index (oldPackages ++ newPackages)
+      indexKeys = List.map fst pairs
+      indexMemberFn :: [BinaryPackage] -> BinaryPackage -> Bool
+      indexMemberFn packages package = any (== (packageID package)) (List.map packageID packages)
+
+-- Repository Accessors and Inquiries
+
+-- | Return a list of all the files in a release which are
+-- 'live', in the sense that they appear in some index files.
+findLive :: MonadInstall m => m (Set Text)
+findLive = do
+    repo <- getL repository <$> getInstall
+    case repoLayout repo of
+      Nothing -> return Set.empty	-- Repository is empty
+      Just layout ->
+          do !releases <- findReleases repo
+             !sourcePackages <- mapM releaseSourcePackages_ releases >>= return . Set.unions
+             !binaryPackages <- mapM releaseBinaryPackages_ releases >>= return . Set.unions
+             let sourceFiles = Set.map (T.pack (outsidePath (repoRoot repo) ++ "/") <>) . Set.map T.pack . Set.fold Set.union Set.empty . Set.map sourceFilePaths_ $ sourcePackages
+             let binaryFiles = Set.map (T.pack (outsidePath (repoRoot repo) ++ "/") <>) . Set.fold (\ mt s -> maybe s (`Set.insert` s) mt) Set.empty $ Set.map (B.fieldValue "Filename" . packageInfo) binaryPackages
+             let changesFiles = Set.map T.pack . Set.fold Set.union Set.empty $ Set.map (Set.fromList . changesFilePaths (repoRoot repo) layout releases) sourcePackages
+             let uploadFiles = Set.map T.pack . Set.fold Set.union Set.empty . Set.map (uploadFilePaths (repoRoot repo) releases) $ sourcePackages
+             return $ Set.unions [sourceFiles, binaryFiles, changesFiles, uploadFiles]
+    where
+      changesFilePaths root Flat releases package =
+          List.map ((outsidePath root ++ "/") ++) . changesFileNames releases $ package
+      changesFilePaths root Pool releases package =
+          List.map ((outsidePath root ++ "/installed/") ++) . changesFileNames releases $ package
+      changesFileNames releases package =
+          List.map (\ arch -> intercalate "_" [ppDisplay (packageName . sourcePackageID $ package),
+                                               show (prettyDebianVersion . packageVersion . sourcePackageID $ package),
+                                               show (prettyArch arch)] ++ ".changes") (toList (architectures releases))
+      uploadFilePaths root releases package = Set.map ((outsidePath root ++ "/") ++) . uploadFileNames releases $ package
+      uploadFileNames releases package =
+          Set.map (\ arch -> intercalate "_" [ppDisplay (packageName . sourcePackageID $ package),
+                                              show (prettyDebianVersion . packageVersion . sourcePackageID $ package),
+                                              show (prettyArch arch)] ++ ".upload") (architectures releases)
+      architectures releases = unions . List.map releaseArchitectures $ releases
+
+instance (Pretty (PP r), Repo r) => Pretty (PP (r, Release, PackageIndex)) where
+    pPrint (PP (repo, r, i)) = text $
+        intercalate "/" [ppDisplay repo,
+                         "dist",
+		         (releaseName' . releaseName $ r),
+		         ppDisplay (packageIndexComponent i),
+                         show (prettyArch (packageIndexArch i))]
+
+instance Pretty (PP (Release, PackageIndex)) where
+    pPrint (PP (r, i)) = text $
+        intercalate "/" [(releaseName' . releaseName $ r),
+		         ppDisplay (packageIndexComponent i),
+                         show (prettyArch (packageIndexArch i))]
+
+instance (Pretty (PP r), Repo r) => Pretty (PP (r, Release)) where
+    pPrint (PP (repo, r)) = hcat [ppPrint repo, text " ", ppPrint r]
+
+instance Pretty (PP Release) where
+    pPrint (PP r) = text $ intercalate " " (releaseName' (releaseName r) : List.map ppDisplay (releaseComponents r))
+
+instance Pretty (PP (Release, PackageIndex, PackageID BinPkgName)) where
+    pPrint (PP (r, i, b)) = text $
+        intercalate "/" [(releaseName' . releaseName $ r),
+		         ppDisplay (packageIndexComponent i),
+                         show (prettyArch (packageIndexArch i)),
+                         ppDisplay b]
+
+instance PkgName name => Pretty (PP (PackageID name)) where
+    pPrint (PP p) = prettyPackageID p -- packageName p ++ "=" ++ show (prettyDebianVersion (packageVersion p))
+
+instance Pretty (PP BinaryPackage) where
+    pPrint (PP p) = ppPrint (packageID p)
+
+-- |Delete any packages from a dist which are trumped by newer
+-- packages.  These packages are not technically garbage because they
+-- can still be installed by explicitly giving their version number to
+-- apt, but it is not really a good idea to use them.
+deleteTrumped :: MonadInstall m => Bool -> Maybe PGPKey -> [Release] -> m [Release]
+deleteTrumped _ _ [] = error "deleteTrumped called with empty release list"
+deleteTrumped dry keyname releases =
+    mapM findTrumped releases >>=
+    return . partitionEithers >>=
+    \ (bad, good) ->
+        case bad of
+          [] -> return (concat good) >>=
+                ifEmpty (qPutStr "deleteTrumped: nothing to delete") >>=
+                deleteSourcePackages dry keyname . (List.map (\ (r, i, p) -> (r, i, packageID p)))
+          _ -> error $ "Error reading package lists"
+    where
+      ifEmpty :: MonadIO m => m () -> [a] -> m [a]
+      ifEmpty action [] = do action; return []
+      ifEmpty _ x = return x
+
+-- | Return a list of packages in a release which are trumped by some
+-- newer version.
+findTrumped :: MonadInstall m => Release -> m (Either String [(Release, PackageIndex, BinaryPackage)])
+findTrumped release =
+    do
+      mapM doIndex (sourceIndexes release) >>= return . merge
+    where
+      doIndex index = getPackages_ release index >>= return . either Left (Right . (List.map (\ b -> (release, index, b))))
+
+-- |Delete any packages from a dist which are trumped by newer
+-- packages.  These packages are not technically garbage because they
+-- can still be installed by explicitly giving their version number to
+-- apt, but it is not really a good idea to use them.
+deleteBinaryOrphans :: MonadInstall m => Bool -> Maybe PGPKey -> [Release] -> m ()
+deleteBinaryOrphans _ _ [] = error "deleteBinaryOrphans called with empty release list"
+deleteBinaryOrphans dry keyname releases =
+    do -- All the source packages in the repository
+       ((exns1, sourcePackages) :: ([[SomeException]], [[[(Release, PackageIndex, SourcePackage)]]])) <- unzip <$> mapM (\ release -> partitionEithers <$> mapM (sourcePackagesOfIndex' release) (sourceIndexes release)) releases
+       -- All the binary packages in the repository
+       ((exns2, binaryPackages) :: ([[SomeException]], [[[(Release, PackageIndex, BinaryPackage)]]])) <- unzip <$> mapM (\ release -> partitionEithers <$> mapM (getPackages' release) (binaryIndexes release)) releases
+       case (concat exns1, concat exns2, concat (concat sourcePackages), concat (concat binaryPackages)) of
+         ([], [], sps, bps) ->
+             do let bps' = Set.fromList (List.map (\ (r, i, b) -> (r, i, packageID b)) bps)
+                qPutStrLn ("Number of source packages: " ++ show (length sps))
+                qPutStrLn ("Number of binary packages: " ++ show (size bps'))
+                let -- The binary packages which are associated with
+                    -- some source package.  These need to have their
+                    -- architecture set from the release architecture
+                    -- list and the source package architecture
+                    goodBps :: Set (Release, PackageIndex, PackageID BinPkgName)
+                    goodBps =
+                        Set.fromList (concatMap f sps)
+                        where
+                          f (r, i, p) = concatMap (g r i) (sourcePackageBinaryIDs_ p)
+                          g r i p' = List.map (h r i p') (toList (releaseArchitectures r))
+                          h r i p' a = (r, i {packageIndexArch = a}, p')
+{-
+                        Set.fromList (concatMap (\ (r, i, p) -> Set.fromList (concatMap (\ bid -> map (\ a -> (r, i {packageindexArch = a}, bid)) (releaseArchitectures r)) (sourcePackageBinaryIDs p)) in
+
+                        Set.fromList (concatMap (\ (r, i, p) -> List.map (\ (a, p') -> (r, (i {packageIndexArch = a}), p')) (concatMap (\ a -> (a, sourcePackageBinaryIDs p)) (releaseArchitectures r))) sps)
+-}
+                    badBps :: Set (Release, PackageIndex, PackageID BinPkgName)
+                    badBps = Set.difference bps' goodBps
+                qPutStrLn ("deleteBinaryOrphans - keeping " ++ show (Set.size goodBps) ++ " packages.")
+                qPutStrLn ("deleteBinaryOrphans - discarding " ++ show (Set.size badBps) ++ " packages.")
+                deleteBinaryPackages dry keyname badBps
+         (exns1', exns2', _, _) -> error $ "Failure(s) loading package indexes:\n " ++ intercalate "\n " (List.map show (exns1' ++ exns2'))
+    where
+      -- p :: (Release, PackageIndex, PackageID BinPkgName) -> Bool
+      -- p (_, _, pid) = isInfixOf "fay" (unBinPkgName . packageName $ pid)
+      getPackages' release index = either Left (Right . List.map (\ p -> (release, index, p))) <$> getPackages_ release index
+      sourcePackagesOfIndex' release index = either Left (Right . List.map (\ p -> (release, index, p))) <$> sourcePackagesOfIndex_ release index
+{-
+         (Right sourcePackages', Right binaryPackages') ->
+             do 
+    mapM (findBinaryOrphans repo) releases >>=
+    return . partitionEithers >>=
+    \ (bad, good) ->
+        case bad of
+          [] -> return (concat good) >>=
+                ifEmpty (qPutStr "deleteBinaryOrphans: nothing to delete") >>=
+                deleteBinaryPackages dry keyname repo . (List.map (\ (r, i, p) -> (r, i, packageID p)))
+          _ -> error $ "Error reading package lists"
+    where
+      ifEmpty :: IO () -> [a] -> IO [a]
+      ifEmpty action [] = do action; return []
+      ifEmpty _ x = return x
+-}
+
+-- | Return a list of binary packages in a release which have no
+-- corresponding source package.  This never ought to happen, but due
+-- to a broken newdist it did.
+{-
+findBinaryOrphans :: LocalRepository -> Release -> IO (Either String [(Release, PackageIndex, BinaryPackage)])
+findBinaryOrphans repo release =
+    mapM (doIndex (sourceIndexList release)) (binaryIndexList release) >>= return . merge
+    where
+      doIndex sourceIndexes binaryIndex =
+          getPackages (repoKey repo) release binaryIndex >>=
+          return . either Left (Right . (List.map (\ b -> (release, binaryIndex, b))))
+-}
+
+merge :: [Either SomeException [(Release, PackageIndex, BinaryPackage)]] -> Either String [(Release, PackageIndex, BinaryPackage)]
+merge packages =
+    case partitionEithers packages of
+      ([], packages') -> Right . concat . List.map tail . List.map newestFirst . groupByName . concat $ packages'
+      (bad, _) -> Left $ "Error(s) reading source indexes: " ++ intercalate ", " (List.map show bad)
+    where
+      groupByName :: [(Release, PackageIndex, BinaryPackage)] -> [[(Release, PackageIndex, BinaryPackage)]]
+      groupByName = groupBy equalNames . sortBy compareNames
+      equalNames (_, _, a') (_, _, b') = packageName (packageID a') == packageName (packageID b')
+      compareNames (_, _, a') (_, _, b') = compare (packageName (packageID a')) (packageName (packageID b'))
+      newestFirst = sortBy (flip compareVersions)
+      compareVersions (_, _, a') (_, _, b') = compare (packageVersion (packageID a')) (packageVersion (packageID b'))
+
+-- | Collect files that no longer appear in any package index and move
+-- them to the removed directory.  The .changes files are treated
+-- specially: they don't appear in any index files, but the package
+-- they belong to can be constructed from their name.
+deleteGarbage :: MonadInstall m => m ()
+deleteGarbage = do
+  layout <- (repoLayout . getL repository) <$> getInstall
+  root <- (repoRoot . getL repository) <$> getInstall
+  case layout of
+      Just layout ->
+          do
+            qPutStrLn ("deleteGarbage in " ++ outsidePath root ++ " (layout=" ++ show layout ++ ")")
+            allFiles1 <- liftIO $ poolFiles root layout
+            allFiles2 <- liftIO $ changesFileList root layout
+            let allFiles = allFiles1 ++ allFiles2
+            -- ePutStr ("allFiles:\n  " ++ intercalate "\n  " (sort allFiles) ++ "\n")
+            liveFiles <- findLive
+            -- ePutStr ("liveFiles:\n  " ++ intercalate "\n  " (sort liveFiles) ++ "\n")
+            let deadFiles = Set.difference (Set.map T.pack (Set.fromList allFiles)) liveFiles
+            qPutStrLn ("Removing:\n  " ++ intercalate "\n  " (Set.toAscList (Set.map T.unpack deadFiles)) ++ "\n")
+            mapM_ (liftIO . moveToRemoved root . T.unpack) (Set.toList deadFiles)
+      _ -> error "Cannot remove files from an empty repository"
+    where
+      poolFiles root Flat = getDirectoryContents (outsidePath root) >>=
+                            filterM (doesFileExist . ((outsidePath root) </>))
+      poolFiles root Pool =
+          getSubPaths (outsidePath root </> "pool") >>=
+          mapM getSubPaths >>= return . concat >>=
+          mapM getSubPaths >>= return . concat >>=
+          mapM getSubPaths >>= return . concat
+      changesFileList root Pool = getDirectoryPaths (outsidePath root ++ "/installed")
+      -- In this case we already got the .changes files from the top directory
+      changesFileList root Flat = getDirectoryPaths (outsidePath root) >>= return . List.filter (isSuffixOf ".changes")
+      getSubPaths path =
+          do
+            isDir <- doesDirectoryExist path
+            case isDir of
+              False -> return [path]
+              True -> getDirectoryPaths path
+      getDirectoryPaths dir = getDirectoryContents dir >>= return . List.filter filterDots >>= return . List.map (dir </>)
+      filterDots "." = False
+      filterDots ".." = False
+      filterDots _ = True
+      -- upload files only appear when we dupload from a flat repository to another.
+      moveToRemoved root file =
+          renameFile file (outsidePath root </> "removed" </> snd (splitFileName file))
+
+-- | Delete specific source packages and their associated binary packages.
+deleteSourcePackages :: MonadInstall m => Bool -> Maybe PGPKey -> [(Release, PackageIndex, PackageID BinPkgName)] -> m [Release]
+deleteSourcePackages _ _ [] = return []
+deleteSourcePackages dry keyname packages =
+    do qPutStrLn ("deleteSourcePackages:\n " ++ intercalate "\n " (List.map (ppDisplay . (\ (_, _, x) -> x)) packages))
+       releases <- (Set.fromList . repoReleaseInfoLocal . getL repository) <$> getInstall
+       mapM doIndex (Set.toList (allIndexes releases))
+    where
+      doIndex (release, index) = getEntries release index >>= put release index . List.partition (victim release index)
+      put :: MonadInstall m => Release -> PackageIndex -> ([BinaryPackage], [BinaryPackage]) -> m Release
+      put release index ([], _) = do
+        qPutStrLn ("deleteSourcePackages - nothing to remove from " ++ show index)
+        return release
+      put release index (junk, keep) = do
+        qPutStrLn ("deleteSourcePackages  - Removing packages from " ++ ppDisplay (release, index) ++ ":\n  " ++ intercalate "\n " (List.map (ppDisplay . packageID) junk))
+        putIndex' keyname release index keep
+      allIndexes releases = Set.fold Set.union Set.empty (Set.map (\ r -> Set.fromList (List.map (r,) (packageIndexes r))) releases) -- concatMap allIndexes (Set.toList indexes)
+      -- (indexes, invalid) = Set.partition (\ (_, i) -> packageIndexArch i == Source) (Set.fromList (List.map (\ (r, i, _) -> (r, i)) (repoReleaseInfoLocal repo)))
+      -- (source, invalid) = Set.partition (\ (r, i, b) -> packageIndexArch i == Source) (Set.fromList packages)
+      -- (indexes, invalid) = Set.partition (\ index -> packageIndexArch index == Source) (Set.fromList (List.map fst packages))
+      -- allIndexes (release, sourceIndex) = packageIndexList release
+
+      -- Compute the id of the source package this entry is from, and see if
+      -- it is one of the packages we are deleting.
+      victim :: Release -> PackageIndex -> BinaryPackage -> Bool
+      victim release index binaryPackage = Set.member (sourceIdent (release, index, binaryPackage)) (Set.fromList packages)
+      getEntries :: MonadInstall m => Release -> PackageIndex -> m [BinaryPackage]
+      getEntries release index = getPackages_ release index >>= return . either (error . show) id
+      putIndex' :: MonadInstall m => Maybe PGPKey -> Release -> PackageIndex -> [BinaryPackage] -> m Release
+      putIndex' keyname release index entries = do
+        -- markReleaseModified
+        repo <- getL repository <$> getInstall
+        case dry of
+          True -> ePutStrLn ("dry run: not changing " ++ show index)
+          False -> liftIO $ putIndex (repoRoot repo) release index entries >> writeRelease repo release >>= signRepo keyname repo
+        return release
+      putIndex :: EnvPath -> Release -> PackageIndex -> [BinaryPackage] -> IO (Either [String] ())
+      putIndex root release index packages =
+                let text = formatControl (B.Control (List.map packageInfo packages)) in
+                liftIO $ writeAndZipFileWithBackup (outsidePath root </> packageIndexPath release index) (L.fromChunks [encodeUtf8 (mconcat text)])
+
+-- | Delete specific source packages and their associated binary packages.
+deleteBinaryPackages :: MonadInstall m => Bool -> Maybe PGPKey -> Set (Release, PackageIndex, PackageID BinPkgName) -> m ()
+deleteBinaryPackages _ _ s | Set.null s = return ()
+deleteBinaryPackages dry keyname blacklist = do
+  repo <- getL repository <$> getInstall
+  mapM_ doIndex (Set.toList (allIndexes repo))
+    where
+      doIndex (release, index) = getEntries release index >>= put release index . List.partition (victim release index)
+      put :: MonadInstall m => Release -> PackageIndex -> ([BinaryPackage], [BinaryPackage]) -> m Release
+      put release index ([], _) =
+          qPutStrLn ("deleteBinaryPackages - nothing to remove from " ++ show index) >>
+          return release
+      put release index (junk, keep) =
+          qPutStrLn ("deleteBinaryPackages - removing " ++ show (length junk) ++ " packages from " ++ ppDisplay (release, index) ++ ", leaving " ++ show (length keep) {- ++ ":\n " ++ intercalate "\n " (List.map (show . F.pretty . packageID) junk) -}) >>
+          putIndex' keyname release index keep
+      allIndexes repo = Set.fold Set.union Set.empty (Set.map (\ r -> Set.fromList (List.map (r,) (packageIndexes r))) (Set.fromList (repoReleaseInfoLocal repo)))
+
+      -- (invalid, indexes) = Set.partition (\ (_, i) -> packageIndexArch i == Source) (Set.fromList (List.map (\ (r, i, _) -> (r, i)) (toList packages)))
+      -- (source, invalid) = Set.partition (\ (r, i, b) -> packageIndexArch i == Source) (Set.fromList packages)
+      -- (indexes, invalid) = Set.partition (\ index -> packageIndexArch index == Source) (Set.fromList (List.map fst packages))
+      -- allIndexes (release, sourceIndex) = packageIndexList release
+
+      -- Compute the id of the source package this entry is from, and see if
+      -- it is one of the packages we are deleting.
+      victim :: Release -> PackageIndex -> BinaryPackage -> Bool
+      victim release index binaryPackage = Set.member (release, index, packageID binaryPackage) blacklist
+
+      getEntries :: MonadInstall m => Release -> PackageIndex -> m [BinaryPackage]
+      getEntries release index = getPackages_ release index >>= return . either (error . show) id
+      putIndex' :: MonadInstall m => Maybe PGPKey -> Release -> PackageIndex -> [BinaryPackage] -> m Release
+      putIndex' keyname release index entries =
+          do repo <- getL repository <$> getInstall
+             case dry of
+               True -> ePutStrLn ("dry run: not changing " ++ show index)
+               False -> liftIO $ putIndex (repoRoot repo) release index entries >> writeRelease repo release >>= signRepo keyname repo
+             return release
+      putIndex :: EnvPath -> Release -> PackageIndex -> [BinaryPackage] -> IO (Either [String] ())
+      putIndex root release index packages =
+                let text = formatControl (B.Control (List.map packageInfo packages)) in
+                liftIO $ writeAndZipFileWithBackup (outsidePath root </> packageIndexPath release index) (L.fromChunks [encodeUtf8 (mconcat text)])
+
+sourceIdent :: (Release, PackageIndex, BinaryPackage) -> (Release, PackageIndex, PackageID BinPkgName)
+sourceIdent (release, index, entry) =
+    case packageIndexArch index of
+      Source -> (release, index, packageID entry)
+      _ -> (release, (index {packageIndexArch = Source}), binaryPackageSourceID_ index entry)
+
+{-
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
+-}
+
+sourceFilePaths_ :: SourcePackage -> Set FilePath
+sourceFilePaths_ package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
+
+toSourcePackage_ :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage_ index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+toBinaryPackage_ :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage_ release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID_ :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID_ (PackageIndex _component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+
+sourcePackageBinaryIDs_ :: SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs_ package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
+
+-- | Get the contents of a package index
+getPackages_ :: MonadInstall m => Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+getPackages_ release index = do
+  repo <- getL repository <$> getInstall
+  let uri = repoKeyURI . repoKey $ repo
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+  liftIO $ fileFromURIStrict uri' >>= readControl uri' . either (Left . SomeException) Right
+    where
+      readControl :: URI -> Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl _ (Left e) = return (Left e)
+      readControl uri' (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage_ release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex_ :: MonadInstall m => Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex_ release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> getPackages_ release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex_ :: MonadInstall m => Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex_ release index =
+    case packageIndexArch index of
+      Source -> getPackages_ release index >>= return . either Left (Right . List.map (toSourcePackage_ index . packageInfo))
+      _ -> return (Right [])
+
+{-
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+-}
+
+-- | Return a list of all source packages.
+releaseSourcePackages_ :: MonadInstall m => Release -> m (Set SourcePackage)
+releaseSourcePackages_ release =
+    mapM (sourcePackagesOfIndex_ release) (sourceIndexes release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages_ :: MonadInstall m => Release -> m (Set BinaryPackage)
+releaseBinaryPackages_ release =
+    mapM (binaryPackagesOfIndex_ release) (binaryIndexes release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Write a set of packages into a package index.
+putPackages_ :: LocalRepository -> Release -> PackageIndex ->  [BinaryPackage] -> IO ()
+putPackages_ repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
+
+{-
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)			-- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")	-- Debugging output
+       return paragraphs
+-}
diff --git a/testdata/debian/Debian/Repo/State/PackageIndex.hs b/testdata/debian/Debian/Repo/State/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/PackageIndex.hs
@@ -0,0 +1,247 @@
+{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, TemplateHaskell #-}
+{-# OPTIONS -Wall -fno-warn-orphans #-}
+module Debian.Repo.State.PackageIndex
+    ( binaryPackagesFromSources
+    , sourcePackagesFromSources
+    ) where
+
+import Control.Monad.Trans (MonadIO(liftIO))
+import Data.List (intercalate)
+import Data.List as List (map, partition)
+import Data.Maybe (catMaybes)
+import qualified Data.Text as T (Text, unpack)
+import Debian.Arch (Arch, Arch(..), prettyArch)
+import Debian.Control (ControlFunctions(stripWS), formatParagraph)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(lookupP), ControlFunctions(parseControlFromHandle), Field, Field'(Field), fieldValue, Paragraph)
+import Debian.Pretty (ppDisplay)
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (ReleaseName(..), releaseName', sectionName')
+import Debian.Repo.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.Internal.Repos (MonadRepos)
+import Debian.Repo.PackageID (makeBinaryPackageID, makeSourcePackageID)
+import Debian.Repo.PackageIndex (BinaryPackage, BinaryPackage(..), PackageIndex(..), PackageIndex(packageIndexArch, packageIndexComponent), packageIndexPath, SourceControl(..), SourceFileSpec(SourceFileSpec), SourcePackage(..), SourcePackage(sourcePackageID))
+--import Debian.Repo.Prelude (symbol)
+import Debian.Repo.Release (Release(releaseName))
+import Debian.Repo.Repo (Repo(repoKey, repoReleaseInfo), RepoKey, repoKeyURI)
+import Debian.Repo.Slice (binarySlices, Slice(sliceRepoKey, sliceSource), SliceList(slices), sourceSlices)
+import Debian.Repo.State.Repository (foldRepository)
+import Debian.Sources (DebSource(sourceDist, sourceType), SourceType(Deb, DebSrc))
+import Debian.URI (URI(uriScheme), uriToString')
+import Debian.Version (parseDebianVersion)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath), URIAuth(uriPort, uriRegName, uriUserInfo))
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+--import System.IO.Unsafe (unsafeInterleaveIO)
+--import System.Process.Progress (qBracket, quieter)
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: MonadRepos m => Arch -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes arch slice =
+    foldRepository f f (sliceRepoKey slice)
+    where
+      f repo =
+          case (sourceDist (sliceSource slice)) of
+            Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+            Right (release, sections) -> return $ map (makeIndex repo release) sections
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> arch })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+data UpdateError
+    = Changed ReleaseName FilePath SliceList SliceList
+    | Missing ReleaseName FilePath
+    | Flushed
+
+instance Show UpdateError where
+    show (Changed r p l1 l2) = unwords ["Changed", show r, show p, ppDisplay l1, ppDisplay l2]
+    show (Missing r p) = unwords ["Missing", show r, show p]
+    show Flushed = "Flushed"
+
+sourcePackagesFromSources :: MonadRepos m => EnvRoot -> Arch -> SliceList -> m [SourcePackage]
+sourcePackagesFromSources root arch sources = do
+  indexes <- mapM (sliceIndexes arch) (slices . sourceSlices $ sources) >>= return . concat
+  mapM (\ (repo, rel, index) -> sourcePackagesOfIndex root arch repo rel index) indexes >>= return . concat
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfIndex :: MonadRepos m => EnvRoot -> Arch -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfIndex root arch repo release index =
+    -- quieter 2 $ qBracket ($(symbol 'sourcePackagesOfIndex) ++ " " ++ path) $
+    do -- unsafeInterleaveIO makes the package index file reads
+       -- asynchronous, not sure what the performance implications
+       -- are.  Anyway, this is now only called on demand, so the
+       -- unsafeInterleaveIO is probably moot.
+       paragraphs <- liftIO $ {-unsafeInterleaveIO-} (readParagraphs path)
+       let packages = List.map (toSourcePackage index) paragraphs
+       return packages
+     where
+       path = rootPath root ++ suff
+       suff = indexCacheFile arch repo release index
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+binaryPackagesFromSources :: MonadRepos m => EnvRoot -> Arch -> SliceList -> m [BinaryPackage]
+binaryPackagesFromSources root arch sources = do
+  indexes <- mapM (sliceIndexes arch) (slices . binarySlices $ sources) >>= return . concat
+  mapM (\ (repo, rel, index) -> binaryPackagesOfIndex root arch repo rel index) indexes >>= return . concat
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfIndex :: MonadRepos m => EnvRoot -> Arch -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfIndex root arch repo release index =
+    -- quieter 2 $ qBracket ($(symbol 'binaryPackagesOfIndex) ++ ": " ++ path) $
+    do paragraphs <- liftIO $ {-unsafeInterleaveIO-} (readParagraphs path)
+       let packages = List.map (toBinaryPackage release index) paragraphs
+       return packages
+    where
+       suff = indexCacheFile arch repo release index
+       path = rootPath root ++ suff
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)			-- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")	-- Debugging output
+       return paragraphs
+
+indexCacheFile :: Arch -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile arch repo release index =
+    case (arch, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, indexArch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch indexArch) ++ "_Packages"
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user'') Nothing (Just host) port' path' =
+          user'' ++ host ++ port' ++ escape path'
+      prefix "http:" _ _ (Just host) port' path' =
+          host ++ port' ++ escape path'
+      prefix "ftp:" _ _ (Just host) _ path' =
+          host ++ escape path'
+      prefix "file:" Nothing Nothing Nothing "" path' =
+          escape path'
+      prefix "ssh:" (Just user'') Nothing (Just host) port' path' =
+          user'' ++ host ++ port' ++ escape path'
+      prefix "ssh" _ _ (Just host) port' path' =
+          host ++ port' ++ escape path'
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s', []) -> [s']
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
diff --git a/testdata/debian/Debian/Repo/State/Release.hs b/testdata/debian/Debian/Repo/State/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/Release.hs
@@ -0,0 +1,267 @@
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, TemplateHaskell #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.State.Release
+    ( flushLocalRepository
+    , prepareRelease
+    , prepareRelease'
+    , findReleases
+    , writeRelease
+    , signRepo
+    , mergeReleases
+    ) where
+
+import Control.Monad.State (filterM, liftM, MonadIO(..))
+import qualified Data.ByteString.Lazy.Char8 as L (empty, readFile)
+import Data.Digest.Pure.MD5 (md5)
+import Data.List as List (group, intercalate, sort)
+import Data.Maybe (catMaybes)
+import Data.Monoid ((<>))
+import Data.Set (Set, toList, unions)
+import Data.Text as T (intercalate, pack, Text)
+import Data.Time (getCurrentTime)
+import Debian.Arch (Arch(..), prettyArch)
+import qualified Debian.Control.Text as S (Control'(Control), ControlFunctions(parseControlFromFile), Field'(Field), fieldValue, Paragraph'(..))
+import Debian.Pretty (ppDisplay)
+import Debian.Release (ReleaseName, releaseName', Section, sectionName')
+import Debian.Repo.EnvPath (outsidePath)
+import Debian.Repo.Internal.Repos (MonadRepos, findRelease, putRelease)
+import qualified Debian.Repo.Prelude.GPGSign as EG (cd, PGPKey, pgpSignFiles)
+import Debian.Repo.LocalRepository (LocalRepository, repoReleaseInfoLocal, repoRoot)
+import Debian.Repo.PackageIndex (PackageIndex(packageIndexArch, packageIndexComponent), packageIndexDir, packageIndexes, packageIndexName, releaseDir)
+import qualified Debian.Repo.Prelude.Files as EF (maybeWriteFile, prepareSymbolicLink, writeAndZipFile)
+import qualified Debian.Repo.Prelude.Time as ET (formatDebianDate)
+import Debian.Repo.Prelude.Verbosity (qPutStrLn)
+import Debian.Repo.Release (parseArchitectures, parseComponents, Release(..))
+import Debian.Repo.Repo (Repo)
+import Debian.Repo.State.Repository (repairLocalRepository)
+import System.Directory (createDirectoryIfMissing, doesFileExist)
+import System.FilePath ((</>))
+import System.Posix.Files (setFileMode)
+import qualified System.Posix.Files as F (fileSize, getFileStatus)
+import System.Unix.Directory (removeRecursiveSafely)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepos m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       r' <- repairLocalRepository r
+       mapM_ (prepareRelease' r') releases
+       repairLocalRepository r'
+    where
+      releases = repoReleaseInfoLocal r
+
+-- The return value might not be the same as the input due to cached
+-- values in the monad.
+prepareRelease' :: MonadRepos m => LocalRepository -> Release -> m Release
+prepareRelease' repo rel = prepareRelease repo (releaseName rel) (releaseAliases rel) (releaseComponents rel) (releaseArchitectures rel)
+
+-- | Find or create a (local) release.
+prepareRelease :: MonadRepos m => LocalRepository -> ReleaseName -> [ReleaseName] -> [Section] -> Set Arch -> m Release
+prepareRelease repo dist aliases sections archSet =
+    -- vPutStrLn 0 ("prepareRelease " ++ name ++ ": " ++ show repo ++ " sections " ++ show sections) >>
+    findRelease repo dist >>= maybe prepare (const prepare) -- return -- JAS - otherwise --create-section does not do anything
+    where
+      prepare :: MonadRepos m => m Release
+      prepare =
+          do -- FIXME: errors get discarded in the mapM calls here
+	     let release = Release { releaseName = dist
+                                   , releaseAliases = aliases
+                                   , releaseComponents = sections
+                                   , releaseArchitectures = archSet }
+             -- vPutStrLn 0 ("packageIndexList: " ++ show (packageIndexList release))
+             _ <- mapM (initIndex (outsidePath root) release) (packageIndexes release)
+             mapM_ (initAlias (outsidePath root) dist) aliases
+             _ <- liftIO (writeRelease repo release)
+	     -- This ought to be identical to repo, but the layout should be
+             -- something rather than Nothing.
+             repo' <- repairLocalRepository repo
+             --vPutStrLn 0 $ "prepareRelease: prepareLocalRepository -> " ++ show repo'
+             putRelease repo' release
+             return release
+      initIndex root' release index = initIndexFile (root' </> packageIndexDir release index) (packageIndexName index)
+      initIndexFile dir name =
+          do liftIO $ createDirectoryIfMissing True dir
+             liftIO $ setFileMode dir 0o040755
+             ensureIndex (dir </> name)
+      initAlias root' dist alias =
+          liftIO $ EF.prepareSymbolicLink (releaseName' dist) (root' </> "dists" </> releaseName' alias)
+      root = repoRoot repo
+
+-- | Make sure an index file exists.
+ensureIndex :: MonadIO m => FilePath -> m (Either [String] ())
+ensureIndex path =
+    do exists <- liftIO $ doesFileExist path
+       case exists of
+         False -> liftIO $ EF.writeAndZipFile path L.empty
+         True -> return $ Right ()
+
+signRepo :: Maybe EG.PGPKey -> LocalRepository -> [FilePath] -> IO ()
+signRepo keyname repo files =
+    do let root = repoRoot repo
+       case keyname of
+         Nothing -> return ()
+         Just key -> do results <- liftIO (EG.pgpSignFiles (outsidePath root) key files)
+                        let failed = catMaybes $ map (\ (path, flag) -> if (not flag) then Just path else Nothing) (zip files results)
+                        case failed of
+                          [] -> return ()
+                          files -> qPutStrLn ("Unable to sign:\n  " ++ List.intercalate "\n  " files)
+
+-- |Write out the @Release@ files that describe a 'Release'.
+writeRelease :: LocalRepository -> Release -> IO [FilePath]
+writeRelease repo release =
+    do let root = repoRoot repo
+       indexReleaseFiles <- liftIO $ writeIndexReleases (outsidePath root) release
+       masterReleaseFile <- writeMasterRelease (outsidePath root) release
+       return (masterReleaseFile : indexReleaseFiles)
+    where
+      writeIndexReleases root release =
+          mapM (writeIndex root release) (packageIndexes release)
+      -- It should only be necessary to write these when the component
+      -- is created, not every time the index files are changed.  But
+      -- for now we're doing it anyway.
+      writeIndex root release index =
+          do let para =
+                     S.Paragraph
+                          [S.Field ("Archive", pack . releaseName' . releaseName $ release),
+                           S.Field ("Component", pack $ sectionName' (packageIndexComponent index)),
+                           S.Field ("Architecture", pack $ show (prettyArch (packageIndexArch index))),
+                           S.Field ("Origin", " SeeReason Partners LLC"),
+                           S.Field ("Label", " SeeReason")] :: S.Paragraph' Text
+             let path = packageIndexDir release index </> "Release"
+             EF.maybeWriteFile (root </> path) (ppDisplay para)
+             return path
+      writeMasterRelease :: FilePath -> Release -> IO FilePath
+      writeMasterRelease root release =
+          do let paths = concat . map (indexPaths release) $ (packageIndexes release)
+             (paths', sums,sizes) <-
+                 liftIO (EG.cd root
+                         (do paths' <- filterM doesFileExist paths
+                             sums <-  mapM (\ path -> L.readFile path >>= return . show . md5) paths'
+                             sizes <- mapM (liftM F.fileSize . F.getFileStatus) paths'
+                             return (paths', sums, sizes)))
+             let checksums = List.intercalate "\n" $ zipWith3 (formatFileInfo (fieldWidth sizes))
+                      	   sums sizes (map (drop (1 + length (releaseDir release))) paths')
+             timestamp <- liftIO (getCurrentTime >>= return . ET.formatDebianDate)
+             let para = S.Paragraph [S.Field ("Origin", " SeeReason Partners"),
+                                     S.Field ("Label", " SeeReason"),
+                                     S.Field ("Suite", " " <> (pack . releaseName' . releaseName $ release)),
+                                     S.Field ("Codename", " " <> (pack . releaseName' . releaseName $ release)),
+                                     S.Field ("Date", " " <> pack timestamp),
+                                     S.Field ("Architectures", " " <> (T.intercalate " " . map (pack . ppDisplay) . toList . releaseArchitectures $ release)),
+                                     S.Field ("Components", " " <> (T.intercalate " " . map (pack . sectionName') . releaseComponents $ release)),
+                                     S.Field ("Description", " SeeReason Internal Use - Not Released"),
+                                     S.Field ("Md5Sum", "\n" <> pack checksums)] :: S.Paragraph' Text
+             let path = "dists" </> (releaseName' . releaseName $ release) </> "Release"
+             liftIO $ EF.maybeWriteFile (root </> path) (ppDisplay para)
+             return path
+      indexPaths release index | packageIndexArch index == Source =
+          map ((packageIndexDir release index) </>) ["Sources", "Sources.gz", "Sources.bz2", "Sources.diff/Index", "Release"]
+      indexPaths release index =
+          map ((packageIndexDir release index) </>) ["Packages", "Packages.gz", "Packages.bz2", "Packages.diff/Index", "Release"]
+      formatFileInfo fw sum size name = List.intercalate " " $ ["",sum, pad ' ' fw $ show size, name]
+      fieldWidth = ceiling . (logBase (10 :: Double)) . fromIntegral . maximum
+
+pad :: Char -> Int -> String -> String
+pad padchar padlen s = replicate p padchar ++ s
+    where p = padlen - length s
+
+-- | Merge a list of releases so each dist only appears once
+mergeReleases :: Repo r => r -> [Release] -> Release
+mergeReleases _repo releases =
+    Release { releaseName = (releaseName . head $ releases)
+            , releaseAliases = aliases
+            , releaseComponents = components
+            , releaseArchitectures = architectures }
+    where
+      aliases = map head . List.group . sort . concat . map releaseAliases $ releases
+      components = map head . group . sort . concat . map releaseComponents $ releases
+      architectures = unions . map releaseArchitectures $ releases
+
+-- | Find all the releases in a repository.
+findReleases :: MonadRepos m => LocalRepository -> m [Release]
+findReleases repo = mapM (findLocalRelease repo) (repoReleaseInfoLocal repo)
+
+findLocalRelease :: MonadRepos m => LocalRepository -> Release -> m Release
+findLocalRelease repo releaseInfo =
+    findRelease repo dist >>= maybe readRelease return
+    where
+      readRelease :: MonadRepos m => m Release
+      readRelease =
+          do let path = (outsidePath (repoRoot repo) </> "dists" </> releaseName' dist </> "Release")
+             info <- liftIO $ S.parseControlFromFile path
+             case info of
+               Right (S.Control (paragraph : _)) ->
+                   case (S.fieldValue "Components" paragraph, S.fieldValue "Architectures" paragraph) of
+                     (Just components, Just architectures) ->
+                         do let rel = (Release
+                                        { releaseName = dist
+                                        , releaseAliases = releaseAliases releaseInfo
+                                        , releaseComponents = parseComponents components
+                                        , releaseArchitectures = parseArchitectures architectures})
+                            putRelease repo rel
+                            return rel
+                     _ ->
+                         error $ "Invalid release file: " ++ path
+               _ -> error $ "Invalid release file: " ++ path
+      dist = releaseName releaseInfo
+
+{-
+CB: Here's my take on what needs to be done based on spelunking through the Debian repositories.
+
+Each component (main, contrib, non-free) has a Release file in the same directory as the Packages files.  These are basically static, consisting of the following fields:
+
+    $ cat /var/www/debian/dists/unstable/non-free/binary-i386/Release
+    Archive: unstable
+    Component: non-free
+    Origin: Debian
+    Label: Debian
+    Architecture: i386
+
+Slightly different for Source:
+
+    $ cat /var/www/debian/dists/unstable/non-free/source/Release
+    Archive: unstable
+    Component: non-free
+    Origin: Debian
+    Label: Debian
+    Architecture: source
+
+Also, if a dist has been released, then a version number is included:
+
+    $ cat /var/www/debian/dists/sarge/non-free/source/Release
+    Archive: stable
+    Version: 3.1r4
+    Component: non-free
+    Origin: Debian
+    Label: Debian
+    Architecture: source
+
+The top level release file is different.  It has the md5sums of all the indices and release files in the distribution.  We need to find out what we should put in for Origin and Label, but I think those are company and OS respectively, so Linspire and Freespire.  It even has a codename...  As before, the version in sarge has a Version number included.
+
+It is this top level Release file that is signed with gpg.
+
+    $ head /var/www/debian/dists/unstable/Release
+    Origin: Debian
+    Label: Debian
+    Suite: unstable
+    Codename: sid
+    Date: Wed, 06 Dec 2006 20:13:03 UTC
+    Architectures: alpha amd64 arm hppa hurd-i386 i386 ia64 m68k mips mipsel powerpc s390 sparc
+    Components: main contrib non-free
+    Description: Debian Unstable - Not Released
+    MD5Sum:
+     397f3216a3a881da263a2fb5e0f8f02e 19487683 main/binary-alpha/Packages
+     c3586c0dcdd6be9266a23537b386185d  5712614 main/binary-alpha/Packages.gz
+     7acf2b8b938dde2c2e484425c806635a  4355026 main/binary-alpha/Packages.bz2
+     dcf0d11800eb007c5435d83c853a241e     2038 main/binary-alpha/Packages.diff/Index
+     3af8fbafe3e4538520989de964289712       83 main/binary-alpha/Release
+     209177470d05f3a50b5217f64613ccca 19747688 main/binary-amd64/Packages
+     a73fd369c6baf422621bfa7170ff1594  5777320 main/binary-amd64/Packages.gz
+     380e56b5acec9ac40f160ea97ea088ef  4403167 main/binary-amd64/Packages.bz2
+     43cbb8ba1631ab35ed94d43380299c38     2038 main/binary-amd64/Packages.diff/Index
+     ab89118658958350f14094b1bc666a62       83 main/binary-amd64/Release
+     d605623ac0d088a8bed287e0df128758 19323166 main/binary-arm/Packages
+
+
+-}
diff --git a/testdata/debian/Debian/Repo/State/Repository.hs b/testdata/debian/Debian/Repo/State/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/Repository.hs
@@ -0,0 +1,129 @@
+-- | A repository located on localhost
+{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, StandaloneDeriving, ScopedTypeVariables, TemplateHaskell, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.State.Repository
+    ( readLocalRepository
+    , prepareLocalRepository
+    , prepareLocalRepository'
+    , prepareRemoteRepository
+    , repairLocalRepository
+    , foldRepository
+    ) where
+
+import Control.Monad (filterM, when)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.Maybe (catMaybes)
+import Debian.Release (ReleaseName(ReleaseName), Section(Section))
+import Debian.Repo.EnvPath (EnvPath, EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Internal.Repos (MonadRepos(..), repoByURI, putRepo)
+import Debian.Repo.LocalRepository (Layout(..), LocalRepository(..), readLocalRepo)
+import Debian.Repo.Release (getReleaseInfoRemote, parseArchitectures, Release(Release, releaseAliases, releaseArchitectures, releaseComponents, releaseName))
+import Debian.Repo.RemoteRepository (RemoteRepository, RemoteRepository(RemoteRepository))
+import Debian.Repo.Repo (RepoKey(..))
+import Debian.URI (fromURI', toURI', URI(uriPath), URI')
+import Network.URI (URI(..))
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.FilePath ((</>))
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, setFileMode)
+import Text.Regex (matchRegex, mkRegex)
+
+repairLocalRepository :: MonadIO m => LocalRepository -> m LocalRepository
+repairLocalRepository r = prepareLocalRepository (repoRoot r) (repoLayout r) (repoReleaseInfoLocal r)
+
+createLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> m (Maybe Layout)
+createLocalRepository root layout = do
+  mapM_ (liftIO . initDir)
+            [(".", 0o40755),
+             ("dists", 0o40755),
+             ("incoming", 0o41755),
+             ("removed", 0o40750),
+             ("reject", 0o40750)]
+  -- If repo exists compute its actual layout
+  layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+  mapM_ (liftIO . initDir)
+            (case layout' of
+               Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+               Just Flat -> []
+               Nothing -> [])
+  return layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+
+readLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> m (Maybe LocalRepository)
+readLocalRepository root layout = createLocalRepository root layout >>= readLocalRepo root
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> [Release] -> m LocalRepository
+prepareLocalRepository root layout releases =
+    readLocalRepository root layout >>= maybe (return $ makeLocalRepo root layout releases) return
+
+prepareLocalRepository' :: MonadIO m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository' root layout =
+    prepareLocalRepository root layout [Release { releaseName = ReleaseName "precise-seereason"
+                                                , releaseAliases = []
+                                                , releaseArchitectures = parseArchitectures "amd64, i386"
+                                                , releaseComponents = [Section "main"] }]
+
+makeLocalRepo :: EnvPath -> Maybe Layout -> [Release] -> LocalRepository
+makeLocalRepo root layout releases =
+    LocalRepository { repoRoot = root
+                    , repoLayout = layout
+                    , repoReleaseInfoLocal = releases }
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+prepareRemoteRepository :: MonadRepos m => URI -> m RemoteRepository
+prepareRemoteRepository uri =
+    let uri' = toURI' uri in
+    repoByURI uri' >>= maybe (loadRemoteRepository uri') return
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+loadRemoteRepository :: MonadRepos m => URI' -> m RemoteRepository
+loadRemoteRepository uri =
+    do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+       let repo = RemoteRepository uri releaseInfo
+       putRepo uri repo
+       return repo
+
+-- foldRepository :: forall m r a. MonadState ReposState m => (r -> m a) -> RepoKey -> m a
+-- foldRepository f key =
+--     case key of
+--       Local path -> prepareLocalRepository path Nothing >>= f
+--       Remote uri' ->
+--           let uri = fromURI' uri' in
+--           case uriScheme uri of
+--             "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= f
+--             _ -> prepareRemoteRepository uri >>= f
+
+foldRepository :: MonadRepos m => (LocalRepository -> m a) -> (RemoteRepository -> m a) -> RepoKey -> m a
+foldRepository f g key =
+    case key of
+      Local path -> readLocalRepository path Nothing >>= maybe (error $ "No repository at " ++ show path) f
+      Remote uri' ->
+          let uri = fromURI' uri' in
+          case uriScheme uri of
+            "file:" -> prepareLocalRepository' (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= f
+            _ -> prepareRemoteRepository uri >>= g
diff --git a/testdata/debian/Debian/Repo/State/Slice.hs b/testdata/debian/Debian/Repo/State/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/State/Slice.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE FlexibleContexts, PackageImports, TemplateHaskell, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.State.Slice
+    ( verifySourcesList
+    , repoSources
+    , updateCacheSources
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad (when)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Monoid ((<>))
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Pretty (ppDisplay)
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Repo.EnvPath (EnvPath(..), EnvRoot(..), outsidePath)
+import Debian.Repo.Internal.Repos (MonadRepos)
+import Debian.Repo.Prelude (replaceFile, symbol)
+import Debian.Repo.Prelude.Verbosity (qPutStrLn)
+import Debian.Repo.Repo (repoKey)
+import Debian.Repo.Slice (NamedSliceList(sliceList, sliceListName), Slice(..), SliceList(..), SourcesChangedAction, doSourcesChangedAction)
+import Debian.Repo.State.Repository (readLocalRepository, prepareRemoteRepository)
+import Debian.Repo.Top (MonadTop, distDir, sourcesPath)
+import Debian.Sources (DebSource(..), SourceType(Deb, DebSrc), parseSourcesList)
+import Debian.URI (dirFromURI, fileFromURI)
+import Network.URI (URI(uriScheme, uriPath))
+import System.Directory (createDirectoryIfMissing, doesFileExist)
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+-- | Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadRepos m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+-- | Make sure all the required local and remote repository objects
+-- used by a sources.list file are in our cache.
+verifySourcesList :: MonadRepos m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifyDebSource :: MonadRepos m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    case uriScheme (sourceUri line) of
+      "file:" -> let path = EnvPath chroot' (uriPath (sourceUri line)) in readLocalRepository path Nothing >>= maybe (error $ "No repository at " ++ show (outsidePath path)) (\ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line})
+      _ -> prepareRemoteRepository (sourceUri line) >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      chroot' = fromMaybe (EnvRoot "") chroot
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.  (FIXME: Does this really work for MonadOS?)
+updateCacheSources :: (MonadRepos m, MonadTop m) => SourcesChangedAction -> NamedSliceList -> m ()
+updateCacheSources sourcesChangedAction baseSources = do
+  let rel = sliceListName baseSources
+  dir <- distDir rel
+  sources <- sourcesPath rel
+  distExists <- liftIO $ doesFileExist sources
+  case distExists of
+    True -> do
+      fileSources <- liftIO (readFile sources) >>= verifySourcesList Nothing . parseSourcesList
+      when (fileSources /= sliceList baseSources)
+           (qPutStrLn ($(symbol 'updateCacheSources) ++ " for " <> show rel) >>
+            liftIO (doSourcesChangedAction dir sources baseSources fileSources sourcesChangedAction))
+    False -> do
+      liftIO $ createDirectoryIfMissing True dir
+      liftIO $ replaceFile sources (ppDisplay baseSources)
+  return ()
diff --git a/testdata/debian/Debian/Repo/Top.hs b/testdata/debian/Debian/Repo/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/Repo/Top.hs
@@ -0,0 +1,54 @@
+-- | The top of a directory which belongs to the client process, used
+-- for temporary storage.  The autobuilder usually assigns the path
+-- "~/.autobuilder", and stores build environments and downloaded
+-- source here.
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    , dists
+    , distDir
+    , sourcesPath
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Monad.Reader (ReaderT(runReaderT), MonadReader(ask))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import Debian.Release (ReleaseName(relName))
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class (Monad m, Functor m) => MonadTop m where
+    askTop :: m FilePath
+
+instance (Monad m, Functor m) => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
+
+dists :: MonadTop m => m FilePath
+dists = sub "dists"
+
+-- | The directory in a repository where the package index files for a
+-- particular dist or release is stored.  (Wait, that's not right.)
+distDir :: MonadTop m => ReleaseName -> m FilePath
+distDir rel = (</> relName rel) <$> dists
+
+-- | The path of the text file containing the sources.list (aka SliceList)
+sourcesPath :: MonadTop m => ReleaseName -> m FilePath
+sourcesPath rel = (</> "sources") <$> distDir rel
diff --git a/testdata/debian/Debian/VersionPolicy.hs b/testdata/debian/Debian/VersionPolicy.hs
new file mode 100644
--- /dev/null
+++ b/testdata/debian/Debian/VersionPolicy.hs
@@ -0,0 +1,291 @@
+{-# LANGUAGE RecordWildCards #-}
+-- |This module contains functions governing the assignment of version
+-- numbers, patterned after Ubuntu version number policy:
+--
+-- (1) To distinguish packages which have been pulled out of one vendor's
+-- repository to build for another, we add a vendor name and a build
+-- number, so version "1.2-3" becomes "1.2-3vendorname1".  Subsequent
+-- builds (perhaps due to changes in build dependencies) would be numbered
+-- "1.2-3vendorname2" and so on.
+--
+-- (2) In addition, packages which are backported, (i.e. built for a
+-- non-development release such as Debian etch or Ubuntu feisty) need
+-- another tag to distinguish them from the version that would go into
+-- the development release (sid or, as of this writing, heron.)  So if
+-- we pulled the source for version "1.2-3vendorname4" out of our pool
+-- to build for feisty, it would become "1.2-3vendorname4~feisty1".
+--
+-- (3) The first two policies combine if we
+-- are building a package pulled directly from the other vendor into
+-- our feisty pool, then "1.2-3" would become 1.2-3vendorname0~feisty1.
+-- Subsequent builds of "1.2-3" (perhaps due to changes in build
+-- dependencies) would get increasing build numbers,
+-- "1.2-3vendorname0~feisty2" etc.
+--
+-- (4) If the original version number does not end with a digit, a "0" is
+-- inserted before the vendor name to facilitate parsing of these tags.
+--
+-- (5) Finally, an additional tag format is supported for the benefit of
+-- one autobuilder client, where before the vendor name an "r" and an
+-- integer are inserted.
+module Debian.VersionPolicy
+    ( VersionTag
+    , parseTag
+    , getTag
+    , dropTag
+    , setTag
+    , appendTag
+    , tagCmp
+    , tagMax
+    , bumpTag
+    , newTag
+    , compareSourceAndDist
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import Debian.Version ( buildDebianVersion, epoch, evr, revision, version, DebianVersion, prettyDebianVersion )
+import Text.Regex ( matchRegex, mkRegex )
+import Data.List ( sortBy )
+import Data.Maybe ( catMaybes, fromMaybe, isJust, isNothing, listToMaybe )
+
+-- |We implement two types of version number tags.  One has the format
+--   @r<releasenumber>vendor<buildnumber>~release<buildnumber>@
+-- the other simply
+--   @vendor<buildnumber>~release<buildnumber>@
+-- There are notes from a meeting that explains why ReleaseTagBuild is
+-- more future friendly, but it is also more ugly.
+data VersionTag
+    = VersionTag { extraNumber :: Maybe Int		-- The number following the "r" (do not
+							-- use in new applications.)
+                 , vendorTag :: (String, Int)		-- The vendor name and build number
+                 , releaseTag :: Maybe (String, Int)	-- The release name and build number
+                 } deriving (Show, Eq)
+
+-- | Parse a Debian revision string (the portion of the version number
+-- following the final dash) into a prefix and a VersionTag.
+parseTag :: [String] -> DebianVersion -> (DebianVersion, Maybe VersionTag)
+parseTag vendors ver =
+    fromMaybe (ver, Nothing) . listToMaybe . filter (isJust . snd) . map (`tryTag` ver) $ vendors
+    where
+      tryTag vendor ver' =
+        let (e, v, r) = evr ver' in
+        let (prefix, tag) =
+                case r of
+                  Nothing -> (Nothing, Nothing)
+                  Just s ->
+                      case matchRegex re s of
+                        Nothing -> (Just s, Nothing)
+                        Just [prefix1, prefix2, buildNo, "", _, _, _, _] ->
+                            (Just (prefix1 ++ prefix2),
+                             Just (VersionTag { extraNumber = Nothing
+                                              , vendorTag = (vendor, read buildNo)
+                                              , releaseTag = Nothing }))
+                        Just [prefix1, prefix2, buildNo, _, releaseName, _, _, releaseNo] ->
+                            (Just (prefix1 ++ prefix2),
+                             Just (VersionTag { extraNumber = Nothing
+                                              , vendorTag = (vendor, read buildNo)
+                                              , releaseTag = Just (releaseName, read releaseNo) }))
+                        Just result -> error $ "Internal error: " ++ show result in
+        -- Try to parse the r5 from the end of the prefix.
+        let (prefix', tag') =
+                case (maybe Nothing (matchRegex extraRE) prefix, tag) of
+                  (Just [prefix1, prefix2, digits], Just tag'') -> 
+                      (Just (prefix1 ++ prefix2), Just (tag'' {extraNumber = Just (read digits)}))
+                  _ -> (prefix, tag) in
+        (buildDebianVersion e v (if prefix' == Just "0" then Nothing else prefix'), tag')
+        where
+          re = mkRegex (prefixRE ++ digitsRE ++ vendorRE ++ "(" ++ releaseRE ++ ")?$")
+          vendorRE = escapeForRegex vendor ++ "([0-9]+)"
+          prefixRE = "^(.*[^0-9])?"
+          digitsRE = "([0-9]+)"
+          releaseRE = "~(([^0-9]+)|(bpo[0-9]+\\+))([0-9]+)"
+          extraRE = mkRegex "^(.*)([0-9]+)r([0-9]+)"
+
+-- Do I have to write this?
+escapeForRegex :: String -> String
+escapeForRegex s =
+    concatMap escape s
+    where escape '+' = "\\+"
+          escape c = [c]
+
+-- | The tag returned by splitTag
+getTag :: [String] -> DebianVersion -> Maybe VersionTag
+getTag vendors ver = snd (parseTag vendors ver)
+
+-- | The prefix returned by splitTag
+dropTag :: [String] -> DebianVersion -> DebianVersion
+dropTag vendors ver = fst (parseTag vendors ver)
+
+-- |Modify a version number by adding or changing the vendor tag.  The
+-- result will be newer than the distVersion (the newest already
+-- uploaded version.)  It will also be different from (though not
+-- necessarily newer than) any of the elements of allVersions
+setTag :: (String -> String)
+       -- ^ The release name alias function.  As an example, this would map
+       -- the repository etch to the tag bpo40+, as specified by Debian policy.
+       -> String
+       -- ^ Other release names we manage that might appear in the repository.
+       -> [String]
+       -- ^ The vendor tag
+       -> Maybe String
+       -- ^ The build release, or Nothing if this is a development release
+       -> Maybe Int
+       -- ^ Use old "r0vendor1" format
+       -> Maybe DebianVersion
+       -- ^ The newst version that is already present in the dist.  We need
+       -- to generate a version number newer than this.
+       -> [DebianVersion]
+       -- ^ All the versions that are currently available in the pool.  The
+       -- result must not be a member of this list.
+       -> DebianVersion
+       -- ^ The version number that appears in the newest change log
+       -- entry of the source package.  The result will be this number
+       -- with a version tag added.  If this version is older than the
+       -- current version with its tag stripped, the package cannot be
+       -- built.
+       -> Failing DebianVersion
+       -- ^ The modified version number
+{-setTag alias vendor oldVendors release extra distVersion allVersions sourceVersion =
+    error ("vendor=" ++ show vendor ++ ", release=" ++ show release ++ ", extra=" ++ show extra ++ ", distVersion=" ++ show distVersion ++ ", allVersions=" ++ show allVersions ++ ", sourceVersion=" ++ show sourceVersion)-}
+setTag alias vendor oldVendors release extra distVersion allVersions sourceVersion =
+    case oldTag of
+      Failure msgs -> Failure msgs
+      Success x -> Success . appendTag alias sourceUpstreamVersion . Just . findAvailableTag . newTag' $ x
+    where
+      oldTag =
+            case maybe Nothing (Just . parseTag (vendor : oldVendors)) distVersion of
+              Nothing -> Success Nothing
+              Just (distUpstreamVersion, distTag) ->
+                  case compare sourceUpstreamVersion distUpstreamVersion of
+                    LT -> Failure ["Source version " ++ show (prettyDebianVersion sourceVersion) ++ " (upstream: " ++ show (prettyDebianVersion sourceUpstreamVersion) ++ ")" ++
+                                   " is too old to trump uploaded version " ++ show (prettyDebianVersion distUpstreamVersion) ++ " (dist: " ++ maybe "Nothing" (show . prettyDebianVersion) distVersion ++ ")"]
+                    GT -> Success Nothing
+                    -- Make sure we have the new vendor tag, not the one that
+                    -- we just parsed.
+                    EQ -> Success (maybe Nothing (Just . fixVendor vendor) distTag)
+      fixVendor vendor' tag = let (_, n) = vendorTag tag in tag {vendorTag = (vendor', n)}
+      -- The new tag must
+      --  1) be newer than the old tag
+      --  2) be at least as new as the tag in the source code changelog.
+      --  3) have the appropriate releaseTag
+      -- The oldTag may not have an appropriate releaseTag, because the
+      -- distribution may have recently switched from development to
+      -- non-development.  In that case we will have to bump the
+      -- vendor build number, not the release build number, and then
+      -- add the release tag.
+      newTag' Nothing =
+          VersionTag {vendorTag = (vendor, 1), releaseTag = maybe Nothing (\ relName -> Just (relName, 1)) release, extraNumber = extra}
+      newTag' (Just distTag) =
+          let distTag' = fixReleaseName release (bumpTag (fixVendor vendor distTag)) in
+          let sourceTag' = maybe Nothing (Just . setReleaseName release) sourceTag in
+          case tagMax [Just distTag', sourceTag'] of
+            Nothing -> error $ "Internal error"
+            Just tag -> tag
+      (sourceUpstreamVersion, sourceTag) = parseTag (vendor : oldVendors) sourceVersion
+      -- All the tags of existing packages whose upstream version matches the source
+      allTags = catMaybes (map snd (filter (\ (v, _) -> v == sourceUpstreamVersion) (map (parseTag (vendor : oldVendors)) allVersions)))
+      -- Repeatedly increment candidate until it differs from the
+      -- elements of all.  Note that this is not the same as taking
+      -- the maximum element and incrementing it, the value we want
+      -- only needs to be not less than candidate.
+      findAvailableTag :: VersionTag -> VersionTag
+      findAvailableTag candidate =
+          if elem candidate allTags then findAvailableTag (bumpTag candidate) else candidate
+
+tagCmp :: Maybe VersionTag -> Maybe VersionTag -> Ordering
+tagCmp (Just tagA) (Just tagB) =
+    let (_, a) = vendorTag tagA
+        (_, b) = vendorTag tagB in
+    case compare a b of
+      EQ -> case (releaseTag tagA, releaseTag tagB) of
+              (Just (_, c), Just (_, d)) -> compare c d
+              (Nothing, Nothing) -> EQ
+              (Nothing, _) -> LT
+              (_, Nothing) -> GT
+      x -> x
+tagCmp Nothing Nothing = EQ
+tagCmp Nothing _ = LT
+tagCmp _ Nothing = GT
+
+tagMax :: [Maybe VersionTag] -> Maybe VersionTag
+tagMax tags = head (sortBy (flip tagCmp) tags)
+
+bumpTag :: VersionTag -> VersionTag
+bumpTag tag@(VersionTag {releaseTag = Just (relName, relBuild)}) = tag {releaseTag = Just (relName, relBuild + 1)}
+bumpTag tag@(VersionTag {vendorTag = (name, build), releaseTag = Nothing}) = tag {vendorTag = (name, build + 1)}
+
+-- If one of the version number candidates has the wrong release name
+-- this function fixes it, ensuring that the new tag isn't trumped by
+-- the old.
+fixReleaseName :: Maybe String -> VersionTag -> VersionTag
+fixReleaseName release tag@(VersionTag {vendorTag = (vendorName, vendorBuild)}) =
+    case (release, releaseTag tag) of
+      (Just relName, Just (oldRelName, _)) | relName == oldRelName -> tag
+      -- If the release name doesn't match we need to bump the vendor build
+      (Just relName, _) -> tag {vendorTag = (vendorName, vendorBuild+1), releaseTag = Just (relName, 1)}
+      -- Removing an existing release tag always increases the version
+      (Nothing, Just _) -> tag {releaseTag = Nothing}
+      (Nothing, Nothing) -> tag {vendorTag = (vendorName, vendorBuild+1)}
+
+-- This is similer to fixReleaseName, but we don't require the result to
+-- trump the argument.  This is applied to the source package version number,
+-- which is not necessarily present in the dist.
+setReleaseName :: Maybe String -> VersionTag -> VersionTag
+setReleaseName release tag =
+    case (release, releaseTag tag) of
+      (Just relName, Just (oldRelName, _)) | relName == oldRelName -> tag
+      (Just relName, Just _) -> tag {releaseTag = Just (relName, 1)}
+      (Just relName, Nothing) -> tag {releaseTag = Just (relName, 1)}
+      (Nothing, _) -> tag {releaseTag = Nothing}
+
+-- Create a tag which is one click newer.
+newTag :: String -> Maybe String -> Maybe Int -> VersionTag
+newTag vendor Nothing extra = VersionTag { extraNumber = extra, vendorTag = (vendor, 1), releaseTag = Nothing }
+newTag vendor (Just name) extra = VersionTag { extraNumber = extra, vendorTag = (vendor, 0), releaseTag = Just (name, 1) }
+
+-- | Format a tag as a string.
+showTag :: (String -> String) -> VersionTag -> String
+showTag alias (VersionTag {..}) =
+   let (vendor, vendorBuildNumber) = vendorTag in
+   maybe "" (("r" ++) . show) extraNumber ++
+         vendor ++ show vendorBuildNumber ++
+         maybe "" (\ (relname, relbuild) -> "~" ++ alias relname ++ show relbuild) releaseTag
+
+-- | Append a vendor tag to a string containing the revision portion
+-- of a debian version number.
+appendTag :: (String -> String) -> DebianVersion -> Maybe VersionTag -> DebianVersion
+appendTag _ ver Nothing = ver
+appendTag alias ver (Just tag) =
+    case revision ver of
+      Nothing -> setRevision ver (Just ("0" ++ showTag alias tag))
+      Just rev -> case matchRegex numericSuffixRE rev of
+                    Just [_, ""] -> setRevision ver (Just (rev ++ "0" ++ showTag alias tag))
+                    Just [_, _] -> setRevision ver (Just (rev ++ showTag alias tag))
+                    _ -> error "internal error"
+    where numericSuffixRE = mkRegex "^(.*[^0-9])?([0-9]*)$"
+
+-- | Change the revision part of a Debian version number.  (This may
+-- belong in Debian.Version.)
+setRevision :: DebianVersion -> Maybe String -> DebianVersion
+setRevision ver rev = buildDebianVersion (epoch ver) (version ver) rev
+
+-- Compare the version seen in the source code changelog to a version
+-- seen in the distribution.  Since the autobuilder adds tags to the
+-- version number of the packages it builds, the distribution version
+-- may have been built from the source version even if they differ.
+-- Specifically, we must assume that if the version matches when we
+-- strip off one or both sections of the tag on the distribution
+-- version number.
+compareSourceAndDist :: [String] -> DebianVersion -> DebianVersion -> Ordering
+compareSourceAndDist vendor s d =
+    let (verS, tagS) = parseTag vendor s
+        (verD, tagD) = parseTag vendor d in
+    let venS = maybe Nothing (Just . vendorTag) tagS
+        venD = maybe Nothing (Just . vendorTag) tagD
+        relS = maybe Nothing releaseTag tagS
+        relD = maybe Nothing releaseTag tagD in
+    case () of
+      _ | verS /= verD -> compare verS verD
+        | isNothing venS && isJust venD -> EQ
+        | isNothing relS && isJust relD -> EQ
+        | True -> compare s d
diff --git a/testdata/fold3b/Main.hs b/testdata/fold3b/Main.hs
new file mode 100644
--- /dev/null
+++ b/testdata/fold3b/Main.hs
@@ -0,0 +1,323 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, OverloadedStrings, RecordWildCards, TemplateHaskell,
+             TypeFamilies, TypeSynonymInstances, OverloadedStrings #-}
+{-# OPTIONS_GHC -F -pgmFhsx2hs #-}
+module Main where
+
+import Happstack.Foundation
+import qualified Data.IxSet as IxSet
+import Data.IxSet (IxSet, Indexable, Proxy(..), (@=), getEQ, getOne, ixSet, ixFun)
+import Data.Text  (Text)
+import qualified Data.Text.Lazy as Lazy
+import qualified Data.Text as Text
+import Data.Time.Clock (UTCTime, getCurrentTime)
+
+------------------------------------------------------------------------------
+-- Model
+------------------------------------------------------------------------------
+
+-- | an id which uniquely identifies a paste
+--
+-- NOTE: 'PasteId 0' indicates that a 'Paste' has not been assigned an
+-- id yet. Though.. I am not thrilled about 0 having special meaning
+-- that is not enforced by the type system.
+newtype PasteId = PasteId { unPasteId :: Integer }
+    deriving (Eq, Ord, Read, Show, Enum, Data, Typeable, SafeCopy)
+$(derivePathInfo ''PasteId)
+
+-- | The format of the paste. Currently we only support plain-text,
+-- but later we might add support for Haskell syntax hightlighting,
+-- etc.
+data Format
+    = PlainText
+      deriving (Eq, Ord, Read, Show, Enum, Bounded, Data, Typeable)
+$(deriveSafeCopy 0 'base ''Format)
+
+-- | the meta-data for a 'Paste'
+--
+-- We break this out separately from the paste, because we often want
+-- only the meta-data. For example, when generating a list of recent pastes.
+data PasteMeta = PasteMeta
+    { pasteId  :: PasteId
+    , title    :: Text
+    , nickname :: Text
+    , format   :: Format
+    , pasted   :: UTCTime
+    }
+    deriving (Eq, Ord, Read, Show, Data, Typeable)
+$(deriveSafeCopy 0 'base ''PasteMeta)
+
+-- | a 'Paste'
+data Paste = Paste
+    { pasteMeta :: PasteMeta
+    , paste     :: Text
+    }
+    deriving (Eq, Ord, Read, Show, Data, Typeable)
+$(deriveSafeCopy 0 'base ''Paste)
+
+-- | The 'Indexable Paste' instance will allow us to create an 'IxSet Paste'
+--
+-- We index on the 'PasteId' and the time it was pasted.
+instance Indexable Paste where
+    empty =
+        ixSet [ ixFun $ (:[]) . pasteId . pasteMeta
+              , ixFun $ (:[]) . pasted  . pasteMeta
+              ]
+
+-- | record to store in acid-state
+data CtrlVState = CtrlVState
+    { pastes      :: IxSet Paste
+    , nextPasteId :: PasteId
+    }
+    deriving (Data, Typeable)
+$(deriveSafeCopy 0 'base ''CtrlVState)
+
+-- | initial value to use with acid-state when no prior state is found
+initialCtrlVState :: CtrlVState
+initialCtrlVState =
+    CtrlVState { pastes      = IxSet.empty
+               , nextPasteId = PasteId 1
+               }
+
+------------------------------------------------------------------------------
+-- Acid-State events
+------------------------------------------------------------------------------
+
+-- | add or update a paste
+--
+-- If the PasteId is '0', then update the paste to use the next unused PasteId and insert it into the IxSet.
+--
+-- Otherwise, we update the existing paste.
+insertPaste :: Paste
+            -> Update CtrlVState PasteId
+insertPaste p@Paste{..}
+    | pasteId pasteMeta == PasteId 0 =
+        do cvs@CtrlVState{..} <- get
+           put $ cvs { pastes = IxSet.insert (p { pasteMeta = pasteMeta { pasteId = nextPasteId }}) pastes
+                     , nextPasteId = succ nextPasteId
+                     }
+           return nextPasteId
+    | otherwise =
+        do cvs@CtrlVState{..} <- get
+           put $ cvs { pastes = IxSet.updateIx (pasteId pasteMeta) p pastes }
+           return (pasteId pasteMeta)
+
+-- | get a paste by it's 'PasteId'
+getPasteById :: PasteId -> Query CtrlVState (Maybe Paste)
+getPasteById pid = getOne . getEQ pid . pastes <$> ask
+
+type Limit  = Int
+type Offset = Int
+
+-- | get recent pastes
+getRecentPastes :: Limit  -- ^ maximum number of recent pastes to return
+                -> Offset -- ^ number of pastes skip (useful for pagination)
+                -> Query CtrlVState [PasteMeta]
+getRecentPastes limit offset =
+    do CtrlVState{..} <- ask
+       return $ map pasteMeta $ take limit $ drop offset $ IxSet.toDescList (Proxy :: Proxy UTCTime) pastes
+
+-- | now we need to tell acid-state which functions should be turn into
+-- acid-state events.
+$(makeAcidic ''CtrlVState
+   [ 'getPasteById
+   , 'getRecentPastes
+   , 'insertPaste
+   ])
+
+------------------------------------------------------------------------------
+-- Route
+------------------------------------------------------------------------------
+
+-- | All the routes for our web application
+data Route
+    = ViewRecent
+    | ViewPaste PasteId
+    | NewPaste
+    | CSS
+      deriving (Eq, Ord, Read, Show, Data, Typeable)
+
+-- | we will just use template haskell to derive the route mapping
+$(derivePathInfo ''Route)
+
+------------------------------------------------------------------------------
+-- CtrlV type-aliases
+------------------------------------------------------------------------------
+
+-- | The foundation types are heavily parameterized -- but for our app
+-- we can pin all the type parameters down.
+type CtrlV'    = FoundationT' Route CtrlVState () IO
+type CtrlV     = XMLGenT CtrlV'
+type CtrlVForm = FoundationForm Route CtrlVState () IO
+
+------------------------------------------------------------------------------
+-- appTemplate
+------------------------------------------------------------------------------
+
+-- | page template function
+appTemplate :: ( EmbedAsChild CtrlV' headers
+               , EmbedAsChild CtrlV' body
+               ) =>
+               Lazy.Text   -- ^ page title
+            -> headers  -- ^ extra headers to add to \<head\> tag
+            -> body     -- ^ contents of \<body\> tag
+            -> CtrlV Response
+appTemplate ttl moreHdrs bdy =
+    do html <- defaultTemplate ttl <%><link rel="stylesheet" href=CSS type="text/css" media="screen" /><% moreHdrs %></%> $
+                <%>
+                 <div id="logo">^V</div>
+                 <ul class="menu">
+                  <li><a href=NewPaste>new paste</a></li>
+                  <li><a href=ViewRecent>recent pastes</a></li>
+                 </ul>
+                 <% bdy %>
+                </%>
+       return $ toResponse html
+
+
+-- | This makes it easy to embed a PasteId in an HSP template
+instance EmbedAsChild CtrlV' PasteId where
+    asChild (PasteId i) = asChild ('#' : show i)
+
+-- | This makes it easy to embed a timestamp into an HSP
+-- template. 'show' provides way too much precision, so something
+-- using formatTime would be better.
+instance EmbedAsChild CtrlV' UTCTime where
+    asChild time = asChild (show time)
+
+------------------------------------------------------------------------------
+-- Pages
+------------------------------------------------------------------------------
+
+-- | page handler for 'ViewRecent'
+viewRecentPage :: CtrlV Response
+viewRecentPage =
+    do method GET
+       recent <- query (GetRecentPastes 20 0)
+       case recent of
+         [] -> appTemplate "Recent Pastes" () <p>There are no pastes yet.</p>
+         _  -> appTemplate "Recent Pastes" () $
+                <%>
+                 <h1>Recent Pastes</h1>
+                 <table>
+                  <thead>
+                   <tr>
+                    <th>id</th>
+                    <th>title</th>
+                    <th>author</th>
+                    <th>date</th>
+                    <th>format</th>
+                   </tr>
+                  </thead>
+                  <tbody>
+                   <% mapM mkTableRow recent %>
+                  </tbody>
+                 </table>
+                </%>
+    where
+      mkTableRow PasteMeta{..} =
+          <tr>
+           <td><a href=(ViewPaste pasteId)><% show $ unPasteId pasteId %></a></td>
+           <td><a href=(ViewPaste pasteId)><% title       %></a></td>
+           <td><% nickname    %></td>
+           <td><% pasted      %></td>
+           <td><% show format %></td>
+          </tr>
+
+-- | page handler for 'ViewPaste'
+viewPastePage :: PasteId -> CtrlV Response
+viewPastePage pid =
+    do method GET
+       mPaste <- query (GetPasteById pid)
+       case mPaste of
+         Nothing ->
+             do notFound ()
+                appTemplate "Paste not found." () $
+                    <p>Paste <% pid %> could not be found.</p>
+         (Just (Paste (PasteMeta{..}) paste)) ->
+             do ok ()
+                appTemplate (Lazy.pack $ "Paste " ++ (show $ unPasteId pid)) () $
+                    <div class="paste">
+                     <dl class="paste-header">
+                      <dt>Paste:</dt><dd><a href=(ViewPaste pid)><% pid %></a></dd>
+                      <dt>Title:</dt><dd><% title %></dd>
+                      <dt>Author:</dt><dd><% nickname %></dd>
+                     </dl>
+                     <div class="paste-body">
+                      <% formatPaste format paste %>
+                     </div>
+                    </div>
+
+-- | convert the paste to HTML. We currently only support 'PlainText',
+-- but eventually it might do syntax hightlighting, markdown, etc.
+--
+-- Note that we do not have to worry about escaping the txt
+-- value.. that is done automatically by HSP.
+formatPaste :: Format -> Text -> CtrlV XML
+formatPaste PlainText txt =
+    <pre><% txt %></pre>
+
+-- | page handler for 'NewPaste'
+newPastePage :: CtrlV Response
+newPastePage =
+    do here <- whereami
+       appTemplate "Add a Paste" () $
+        <%>
+         <h1>Add a paste</h1>
+         <% reform (form here) "add" success Nothing pasteForm %>
+        </%>
+    where
+      success :: Paste -> CtrlV Response
+      success paste =
+          do pid <- update (InsertPaste paste)
+             seeOtherURL (ViewPaste pid)
+
+-- | the 'Form' used for entering a new paste
+pasteForm :: CtrlVForm Paste
+pasteForm =
+    (fieldset $
+       ul $
+          (,,,) <$> (li $ label <span>title</span>  ++> (inputText "" `transformEither` required) <++ errorList)
+                <*> (li $ label <span>nick</span>   ++> (inputText "" `transformEither` required) <++ errorList)
+                <*> (li $ label <span>format</span> ++> formatForm)
+                <*> (li $ label <div>paste</div>    ++> errorList ++> (textarea 80 25 "" `transformEither` required))
+                <* inputSubmit "paste!"
+    )  `transformEitherM` toPaste
+    where
+      formatForm =
+          select [(a, show a) | a <- [minBound .. maxBound]] (== PlainText)
+      toPaste (ttl, nick, fmt, bdy) =
+          do now <- liftIO getCurrentTime
+             return $ Right $
+                        (Paste { pasteMeta = PasteMeta { pasteId  = PasteId 0
+                                                       , title    = ttl
+                                                       , nickname = nick
+                                                       , format   = fmt
+                                                       , pasted   = now
+                                                       }
+                               , paste = bdy
+                               })
+      required txt
+          | Text.null txt = Left "Required"
+          | otherwise     = Right txt
+
+------------------------------------------------------------------------------
+-- route
+------------------------------------------------------------------------------
+
+-- | the route mapping function
+route :: Route -> CtrlV Response
+route url =
+    case url of
+      ViewRecent      -> viewRecentPage
+      (ViewPaste pid) -> viewPastePage pid
+      NewPaste        -> newPastePage
+      CSS             -> serveFile (asContentType "text/css") "style.css"
+
+------------------------------------------------------------------------------
+-- main
+------------------------------------------------------------------------------
+
+-- | start the app. listens on port 8000 on localhost
+main :: IO ()
+main = simpleApp id defaultConf (AcidLocal Nothing initialCtrlVState) () ViewRecent "" route
diff --git a/testdata/fold5.hs b/testdata/fold5.hs
new file mode 100644
--- /dev/null
+++ b/testdata/fold5.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
+module Data.Logic.Classes.Literal where
+
+a = 1 where
+
+{- This makes bad things happen. -}
+
+b = ()
diff --git a/testdata/fold9.hs b/testdata/fold9.hs
new file mode 100644
--- /dev/null
+++ b/testdata/fold9.hs
@@ -0,0 +1,2 @@
+{-# OPTIONS_GHC Arrows #-}
+import System.Process (proc)
diff --git a/testdata/imports3/NotMain.hs b/testdata/imports3/NotMain.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports3/NotMain.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = return ()
diff --git a/testdata/imports4/Hiding.hs b/testdata/imports4/Hiding.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports4/Hiding.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Hiding where
+
+import Prelude hiding (writeFile, unlines)
+import Data.Text (unlines)
+import Data.Text.IO (writeFile)
+
+main :: IO ()
+main = writeFile "/tmp/foo" (unlines ["hello", "world"])
diff --git a/testdata/imports5/Deriving.hs b/testdata/imports5/Deriving.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports5/Deriving.hs
@@ -0,0 +1,9 @@
+module Deriving where
+
+import Data.Text (Text)
+import Debian.Control (Paragraph(..), Paragraph'(..), Field'(..))
+
+{-
+deriving instance Show (Field' String)
+deriving instance Show Paragraph
+-}
diff --git a/testdata/imports6-expected/EndComment.hs b/testdata/imports6-expected/EndComment.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports6-expected/EndComment.hs
@@ -0,0 +1,9 @@
+module Test where
+
+
+
+{- This is a comment before a -}
+
+a = 1
+
+{- This is a comment near EOF -}
diff --git a/testdata/imports6/EndComment.hs b/testdata/imports6/EndComment.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports6/EndComment.hs
@@ -0,0 +1,9 @@
+module Test where
+
+import Data.List (dropWhile)
+
+{- This is a comment before a -}
+
+a = 1
+
+{- This is a comment near EOF -}
diff --git a/testdata/imports7-expected/CLI.hs b/testdata/imports7-expected/CLI.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports7-expected/CLI.hs
@@ -0,0 +1,87 @@
+{-# OPTIONS_GHC -Wall #-}
+module Main where
+
+import Control.Monad as List (mapM_)
+import Control.Monad.Trans (MonadIO(liftIO))
+import Data.List (intercalate, isPrefixOf)
+import Data.Set.Extra as Set (Set, toList)
+import Language.Haskell.Modules (cleanImports, findHsModules, mergeModules, modifyDirs, ModuleName(..), MonadClean, noisily, putDirs, putModule, quietly, runCleanT, splitModuleDecls)
+import Language.Haskell.Modules.ModuVerse (getNames)
+import Language.Haskell.Modules.SourceDirs (getDirs)
+import System.IO (hGetLine, hPutStr, hPutStrLn, stderr, stdin)
+
+main :: IO ()
+main = runCleanT (noisily cli)
+
+cli :: MonadClean m => m ()
+cli = liftIO (hPutStr stderr " > " >> hGetLine stdin) >>= cmd . words
+
+cmd :: MonadClean m => [String] -> m ()
+cmd [] = cli
+cmd (s : args) =
+    case filter (any (== s) . fst) cmds of
+      [(_, f)] -> f
+      -- No exact matches - look for prefix matches
+      [] -> case filter (any (isPrefixOf s) . fst) cmds of
+              [(_, f)] -> f
+              [] -> liftIO (hPutStrLn stderr $
+                              show s ++ " invalid - expected " ++ intercalate ", " (concatMap fst cmds)) >> cli
+              xs -> liftIO (hPutStrLn stderr $
+                              show s ++ " ambiguous - expected " ++ intercalate ", " (concatMap fst xs)) >> cli
+      _ -> error $ "Internal error - multiple definitions for " ++ show s
+    where
+      cmds =
+          [(["quit", "exit", "bye", "."],	liftIO (hPutStrLn stderr "Exiting")),
+           (["v"],				liftIO (hPutStrLn stderr "Increasing verbosity level") >> noisily cli),
+           (["q"],				liftIO (hPutStrLn stderr "Decreasing verbosity level") >> quietly cli),
+           (["help"],				liftIO (hPutStrLn stderr "help text") >> cli),
+           (["verse"],				verse args >> cli),
+           (["clean"],				clean args >> cli),
+           (["dir"],				dir args >> cli),
+           (["split"],				split args >> cli),
+           (["merge"],				merge args >> cli)]
+
+unModuleName :: ModuleName -> String
+unModuleName (ModuleName x) = x
+
+verse :: MonadClean m => [String] -> m ()
+verse [] =
+    do modules <- getNames
+       liftIO $ hPutStrLn stderr ("Usage: verse <pathormodule1> <pathormodule2> ...\n" ++
+                                  "Add the module or all the modules below a directory to the moduVerse\n" ++
+                                  "Currently:\n  " ++ showVerse modules)
+verse args =
+    do new <- mapM (liftIO . find) args
+       List.mapM_ (List.mapM_ putModule) new
+       modules <- getNames
+       liftIO (hPutStrLn stderr $ "moduVerse updated:\n  " ++ showVerse modules)
+    where
+      find s =
+          do ms <- liftIO (findHsModules [s])
+             case ms of
+               [] -> return [ModuleName s]
+               _ -> return ms
+
+showVerse :: Set ModuleName -> String
+showVerse modules = "[ " ++ intercalate "\n  , " (map unModuleName (toList modules)) ++ " ]"
+
+dir :: MonadClean m => [FilePath] -> m ()
+dir [] = putDirs []
+dir xs =
+    do modifyDirs (++ xs)
+       xs' <- getDirs
+       liftIO (hPutStrLn stderr $ "sourceDirs updated:\n  [ " ++ intercalate "\n  , " xs' ++ " ]")
+
+clean :: MonadClean m => [FilePath] -> m ()
+clean [] = liftIO $ hPutStrLn stderr "Usage: clean <modulepath1> <modulepath2> ..."
+clean args = cleanImports args >> return ()
+
+split :: MonadClean m => [FilePath] -> m ()
+split [arg] = splitModuleDecls arg >> return ()
+split _ = liftIO $ hPutStrLn stderr "Usage: split <modulepath>"
+
+merge :: MonadClean m => [String] -> m ()
+merge args =
+    case splitAt (length args - 1) args of
+      (inputs, [output]) -> mergeModules (map ModuleName inputs) (ModuleName output) >> return ()
+      _ -> liftIO $ hPutStrLn stderr "Usage: merge <inputmodulename1> <inputmodulename2> ... <outputmodulename>"
diff --git a/testdata/imports7/CLI.hs b/testdata/imports7/CLI.hs
new file mode 100644
--- /dev/null
+++ b/testdata/imports7/CLI.hs
@@ -0,0 +1,87 @@
+{-# OPTIONS_GHC -Wall #-}
+module Main where
+
+import Control.Monad as List (mapM_)
+import Control.Monad.Trans (MonadIO(liftIO))
+import Data.List (intercalate, isPrefixOf)
+import Data.Set.Extra as Set (Set, toList)
+import Language.Haskell.Modules
+import Language.Haskell.Modules.ModuVerse (getNames)
+import Language.Haskell.Modules.SourceDirs (getDirs)
+import System.IO (hGetLine, hPutStr, hPutStrLn, stderr, stdin)
+
+main :: IO ()
+main = runCleanT (noisily cli)
+
+cli :: MonadClean m => m ()
+cli = liftIO (hPutStr stderr " > " >> hGetLine stdin) >>= cmd . words
+
+cmd :: MonadClean m => [String] -> m ()
+cmd [] = cli
+cmd (s : args) =
+    case filter (any (== s) . fst) cmds of
+      [(_, f)] -> f
+      -- No exact matches - look for prefix matches
+      [] -> case filter (any (isPrefixOf s) . fst) cmds of
+              [(_, f)] -> f
+              [] -> liftIO (hPutStrLn stderr $
+                              show s ++ " invalid - expected " ++ intercalate ", " (concatMap fst cmds)) >> cli
+              xs -> liftIO (hPutStrLn stderr $
+                              show s ++ " ambiguous - expected " ++ intercalate ", " (concatMap fst xs)) >> cli
+      _ -> error $ "Internal error - multiple definitions for " ++ show s
+    where
+      cmds =
+          [(["quit", "exit", "bye", "."],	liftIO (hPutStrLn stderr "Exiting")),
+           (["v"],				liftIO (hPutStrLn stderr "Increasing verbosity level") >> noisily cli),
+           (["q"],				liftIO (hPutStrLn stderr "Decreasing verbosity level") >> quietly cli),
+           (["help"],				liftIO (hPutStrLn stderr "help text") >> cli),
+           (["verse"],				verse args >> cli),
+           (["clean"],				clean args >> cli),
+           (["dir"],				dir args >> cli),
+           (["split"],				split args >> cli),
+           (["merge"],				merge args >> cli)]
+
+unModuleName :: ModuleName -> String
+unModuleName (ModuleName x) = x
+
+verse :: MonadClean m => [String] -> m ()
+verse [] =
+    do modules <- getNames
+       liftIO $ hPutStrLn stderr ("Usage: verse <pathormodule1> <pathormodule2> ...\n" ++
+                                  "Add the module or all the modules below a directory to the moduVerse\n" ++
+                                  "Currently:\n  " ++ showVerse modules)
+verse args =
+    do new <- mapM (liftIO . find) args
+       List.mapM_ (List.mapM_ putModule) new
+       modules <- getNames
+       liftIO (hPutStrLn stderr $ "moduVerse updated:\n  " ++ showVerse modules)
+    where
+      find s =
+          do ms <- liftIO (findHsModules [s])
+             case ms of
+               [] -> return [ModuleName s]
+               _ -> return ms
+
+showVerse :: Set ModuleName -> String
+showVerse modules = "[ " ++ intercalate "\n  , " (map unModuleName (toList modules)) ++ " ]"
+
+dir :: MonadClean m => [FilePath] -> m ()
+dir [] = putDirs []
+dir xs =
+    do modifyDirs (++ xs)
+       xs' <- getDirs
+       liftIO (hPutStrLn stderr $ "sourceDirs updated:\n  [ " ++ intercalate "\n  , " xs' ++ " ]")
+
+clean :: MonadClean m => [FilePath] -> m ()
+clean [] = liftIO $ hPutStrLn stderr "Usage: clean <modulepath1> <modulepath2> ..."
+clean args = cleanImports args >> return ()
+
+split :: MonadClean m => [FilePath] -> m ()
+split [arg] = splitModuleDecls arg >> return ()
+split _ = liftIO $ hPutStrLn stderr "Usage: split <modulepath>"
+
+merge :: MonadClean m => [String] -> m ()
+merge args =
+    case splitAt (length args - 1) args of
+      (inputs, [output]) -> mergeModules (map ModuleName inputs) (ModuleName output) >> return ()
+      _ -> liftIO $ hPutStrLn stderr "Usage: merge <inputmodulename1> <inputmodulename2> ... <outputmodulename>"
diff --git a/testdata/logic/Data/Boolean.hs b/testdata/logic/Data/Boolean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Boolean.hs
@@ -0,0 +1,157 @@
+{-# OPTIONS -fno-warn-incomplete-patterns #-}
+-- |
+-- Module      : Data.Boolean
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This library provides a representation of boolean formulas that is
+-- used by the solver in "Data.Boolean.SatSolver".
+-- 
+-- We also define a function to simplify formulas, a type for
+-- conjunctive normalforms, and a function that creates them from
+-- boolean formulas.
+-- 
+module Data.Boolean ( 
+
+  Boolean(..), 
+
+  Literal(..), literalVar, invLiteral, isPositiveLiteral, 
+
+  CNF, Clause, booleanToCNF
+
+  ) where
+
+import Data.Maybe ( mapMaybe )
+import qualified Data.IntMap as IM
+
+import Control.Monad ( guard, liftM )
+
+-- | Boolean formulas are represented as values of type @Boolean@.
+-- 
+data Boolean
+  -- | Variables are labeled with an @Int@,
+  = Var Int
+  -- | @Yes@ represents /true/,
+  | Yes
+  -- | @No@ represents /false/,
+  | No
+  -- | @Not@ constructs negated formulas,
+  | Not Boolean
+  -- | and finally we provide conjunction
+  | Boolean :&&: Boolean
+  -- | and disjunction of boolean formulas.
+  | Boolean :||: Boolean
+ deriving Show
+
+-- | Literals are variables that occur either positively or negatively.
+-- 
+data Literal = Pos Int | Neg Int deriving (Eq, Show)
+
+-- | This function returns the name of the variable in a literal.
+-- 
+literalVar :: Literal -> Int
+literalVar (Pos n) = n
+literalVar (Neg n) = n
+
+-- | This function negates a literal.
+-- 
+invLiteral :: Literal -> Literal
+invLiteral (Pos n) = Neg n
+invLiteral (Neg n) = Pos n
+
+-- | This predicate checks whether the given literal is positive.
+-- 
+isPositiveLiteral :: Literal -> Bool
+isPositiveLiteral (Pos _) = True
+isPositiveLiteral _       = False
+
+-- | Conjunctive normalforms are lists of lists of literals.
+-- 
+type CNF     = [Clause]
+type Clause  = [Literal]
+
+-- | 
+-- We convert boolean formulas to conjunctive normal form by pushing
+-- negations down to variables and repeatedly applying the
+-- distributive laws.
+-- 
+booleanToCNF :: Boolean -> CNF
+booleanToCNF
+  = mapMaybe (simpleClause . map literal . disjunction)
+  . conjunction
+  . asLongAsPossible distribute
+  . asLongAsPossible pushNots
+  . asLongAsPossible elim
+ where
+  elim (Not Yes)      = Just No
+  elim (Not No)       = Just Yes
+  elim (No  :&&: _)   = Just No
+  elim (Yes :&&: x)   = Just x
+  elim (_   :&&: No)  = Just No
+  elim (x   :&&: Yes) = Just x 
+  elim (Yes :||: _)   = Just Yes
+  elim (No  :||: x)   = Just x
+  elim (_   :||: Yes) = Just Yes
+  elim (x   :||: No)  = Just x
+  elim _              = Nothing
+
+  pushNots (Not (Not x))  = Just x
+  pushNots (Not (x:&&:y)) = Just (Not x :||: Not y)
+  pushNots (Not (x:||:y)) = Just (Not x :&&: Not y)
+  pushNots _              = Nothing
+
+  distribute (x:||:(y:&&:z)) = Just ((x:||:y):&&:(x:||:z))
+  distribute ((x:&&:y):||:z) = Just ((x:||:z):&&:(y:||:z))
+  distribute _               = Nothing
+
+  literal (Var x)       = Pos x
+  literal (Not (Var x)) = Neg x
+
+
+-- private helper functions
+
+-- remove duplicate literals from clauses and drop clauses that
+-- contain one literal both positively and negatively.
+--
+simpleClause :: Clause -> Maybe Clause
+simpleClause = liftM (map lit . IM.toList) . foldl add (Just IM.empty)
+ where
+  lit (x,True)  = Pos x
+  lit (x,False) = Neg x
+
+  add mm l = do
+    m <- mm
+    let x = literalVar l; kind = isPositiveLiteral l
+    maybe (Just (IM.insert x kind m))
+          (\b -> guard (b==kind) >> Just m)
+          (IM.lookup x m)
+
+conjunction :: Boolean -> [Boolean]
+conjunction b = flat b []
+ where flat Yes      = id
+       flat (x:&&:y) = flat x . flat y
+       flat x        = (x:)
+
+disjunction :: Boolean -> [Boolean]
+disjunction b = flat b []
+ where flat No       = id
+       flat (x:||:y) = flat x . flat y
+       flat x        = (x:)
+
+asLongAsPossible :: (Boolean -> Maybe Boolean) -> Boolean -> Boolean
+asLongAsPossible f = everywhere g
+ where g x = maybe x (everywhere g) (f x)
+
+everywhere :: (Boolean -> Boolean) -> Boolean -> Boolean
+everywhere f = f . atChildren (everywhere f)
+
+atChildren :: (Boolean -> Boolean) -> Boolean -> Boolean
+atChildren f (Not x)  = Not (f x)
+atChildren f (x:&&:y) = f x :&&: f y
+atChildren f (x:||:y) = f x :||: f y
+atChildren _ x        = x
+
diff --git a/testdata/logic/Data/Boolean/SatSolver.hs b/testdata/logic/Data/Boolean/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Boolean/SatSolver.hs
@@ -0,0 +1,183 @@
+-- |
+-- Module      : Data.Boolean.SatSolver
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This Haskell library provides an implementation of the
+-- Davis-Putnam-Logemann-Loveland algorithm
+-- (cf. <http://en.wikipedia.org/wiki/DPLL_algorithm>) for the boolean
+-- satisfiability problem. It not only allows to solve boolean
+-- formulas in one go but also to add constraints and query bindings
+-- of variables incrementally.
+-- 
+-- The implementation is not sophisticated at all but uses the basic
+-- DPLL algorithm with unit propagation.
+-- 
+module Data.Boolean.SatSolver (
+
+  Boolean(..), SatSolver, Literal(..), literalVar, invLiteral, isPositiveLiteral, CNF, Clause, booleanToCNF,
+
+  newSatSolver, isSolved, 
+
+  lookupVar, assertTrue, assertTrue', branchOnVar, selectBranchVar, solve, isSolvable
+
+  ) where
+
+import Data.List
+import Data.Boolean
+
+import Control.Monad.Writer
+
+import qualified Data.IntMap as IM
+
+-- | A @SatSolver@ can be used to solve boolean formulas.
+-- 
+data SatSolver = SatSolver { clauses :: CNF, bindings :: IM.IntMap Bool }
+ deriving Show
+
+-- | A new SAT solver without stored constraints.
+-- 
+newSatSolver :: SatSolver
+newSatSolver = SatSolver [] IM.empty
+
+-- | This predicate tells whether all constraints are solved.
+-- 
+isSolved :: SatSolver -> Bool
+isSolved = null . clauses
+
+-- |
+-- We can lookup the binding of a variable according to the currently
+-- stored constraints. If the variable is unbound, the result is
+-- @Nothing@.
+-- 
+lookupVar :: Int -> SatSolver -> Maybe Bool
+lookupVar name = IM.lookup name . bindings
+
+-- | 
+-- We can assert boolean formulas to update a @SatSolver@. The
+-- assertion may fail if the resulting constraints are unsatisfiable.
+-- 
+assertTrue :: MonadPlus m => Boolean -> SatSolver -> m SatSolver
+assertTrue formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      (booleanToCNF formula)
+  simplify (solver { clauses = newClauses })
+
+assertTrue' :: MonadPlus m => CNF -> SatSolver -> m SatSolver
+assertTrue' formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      formula
+  simplify (solver { clauses = newClauses })
+
+-- |
+-- This function guesses a value for the given variable, if it is
+-- currently unbound. As this is a non-deterministic operation, the
+-- resulting solvers are returned in an instance of @MonadPlus@.
+-- 
+branchOnVar :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnVar name solver =
+  maybe (branchOnUnbound name solver)
+        (const (return solver))
+        (lookupVar name solver)
+
+-- |
+-- We select a variable from the shortest clause hoping to produce a
+-- unit clause.
+--
+selectBranchVar :: SatSolver -> Int
+selectBranchVar = literalVar . head . head . sortBy shorter . clauses
+
+-- | 
+-- This function guesses values for variables such that the stored
+-- constraints are satisfied. The result may be non-deterministic and
+-- is, hence, returned in an instance of @MonadPlus@.
+-- 
+solve :: MonadPlus m => SatSolver -> m SatSolver
+solve solver
+  | isSolved solver = return solver
+  | otherwise = branchOnUnbound (selectBranchVar solver) solver >>= solve
+
+-- |
+-- This predicate tells whether the stored constraints are
+-- solvable. Use with care! This might be an inefficient operation. It
+-- tries to find a solution using backtracking and returns @True@ if
+-- and only if that fails.
+-- 
+isSolvable :: SatSolver -> Bool
+isSolvable = not . null . solve
+
+
+-- private helper functions
+
+addClause :: MonadPlus m => IM.IntMap Bool -> m [Clause] -> Clause -> m [Clause]
+addClause binds mclauses newClause = do
+  oldClauses <- mclauses
+  let unboundLits = foldl (addUnbound binds) (Just []) newClause
+  maybe (return oldClauses)
+        (\lits -> guard (not (null lits)) >> return (lits:oldClauses))
+        unboundLits
+
+addUnbound :: IM.IntMap Bool -> Maybe Clause -> Literal -> Maybe Clause
+addUnbound binds mlits lit = do
+  lits <- mlits
+  maybe (Just (lit:lits))
+        (\b -> guard (b /= isPositiveLiteral lit) >> return lits)
+        (IM.lookup (literalVar lit) binds)
+
+updateSolver :: MonadPlus m => CNF -> [(Int,Bool)] -> SatSolver -> m SatSolver
+updateSolver cs bs solver = do
+  bs' <- foldr (uncurry insertBinding) (return (bindings solver)) bs
+  return $ solver { clauses = cs, bindings = bs' }
+
+insertBinding :: MonadPlus m
+              => Int -> Bool -> m (IM.IntMap Bool) -> m (IM.IntMap Bool)
+insertBinding name newValue binds = do
+  bs <- binds
+  maybe (return (IM.insert name newValue bs))
+        (\oldValue -> do guard (oldValue==newValue); return bs)
+        (IM.lookup name bs)
+
+simplify :: MonadPlus m => SatSolver -> m SatSolver
+simplify solver = do
+  (cs,bs) <- runWriterT . simplifyClauses . clauses $ solver
+  updateSolver cs bs solver
+
+simplifyClauses :: MonadPlus m => CNF -> WriterT [(Int,Bool)] m CNF
+simplifyClauses [] = return []
+simplifyClauses allClauses = do
+  let shortestClause = head . sortBy shorter $ allClauses
+  guard (not (null shortestClause))
+  if null (tail shortestClause)
+   then propagate (head shortestClause) allClauses >>= simplifyClauses
+   else return allClauses
+
+propagate :: MonadPlus m => Literal -> CNF -> WriterT [(Int,Bool)] m CNF
+propagate literal allClauses = do
+  tell [(literalVar literal, isPositiveLiteral literal)]
+  return (foldr prop [] allClauses)
+ where
+  prop c cs | literal `elem` c = cs
+            | otherwise        = filter (invLiteral literal/=) c : cs
+
+branchOnUnbound :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnUnbound name solver =
+  guess (Pos name) solver `mplus` guess (Neg name) solver
+
+guess :: MonadPlus m => Literal -> SatSolver -> m SatSolver
+guess literal solver = do
+  (cs,bs) <- runWriterT (propagate literal (clauses solver) >>= simplifyClauses)
+  updateSolver cs bs solver
+
+shorter :: [a] -> [a] -> Ordering
+shorter []     []     = EQ
+shorter []     _      = LT
+shorter _      []     = GT
+shorter (_:xs) (_:ys) = shorter xs ys
+
+
diff --git a/testdata/logic/Data/Logic/Classes/Apply.hs b/testdata/logic/Data/Logic/Classes/Apply.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Apply.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures #-}
+-- | The Apply class represents a type of atom the only supports predicate application.
+module Data.Logic.Classes.Apply
+    ( Apply(..)
+    , Predicate
+    , apply
+    , zipApplys
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , showApply
+    , prettyApply
+    , varApply
+    , substApply
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Term (Term, showTerm, prettyTerm, fvt, tsubst)
+import Data.List (intercalate, intersperse)
+import Data.Maybe (fromMaybe)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), text, empty, parens, cat)
+
+class (Arity p, Constants p, Eq p, Ord p, Data p, Pretty p) => Predicate p
+
+class Predicate p => Apply atom p term | atom -> p term where
+    foldApply :: (p -> [term] -> r) -> (Bool -> r) -> atom -> r
+    apply' :: p -> [term] -> atom
+
+-- | apply' with an arity check - clients should always call this.
+apply :: Apply atom p term => p -> [term] -> atom
+apply p ts =
+    case arity p of
+      Just n | n /= length ts -> error "arity"
+      _ -> apply' p ts
+
+zipApplys :: Apply atom p term =>
+            (p -> [term] -> p -> [term] -> Maybe r)
+         -> (Bool -> Bool -> Maybe r)
+         -> atom -> atom -> Maybe r
+zipApplys ap tf a1 a2 =
+    foldApply ap' tf' a1
+    where
+      ap' p1 ts1 = foldApply (ap p1 ts1) (\ _ -> Nothing) a2
+      tf' x1 = foldApply (\ _ _ -> Nothing) (tf x1) a2
+
+apply0 p = if fromMaybe 0 (arity p) == 0 then apply' p [] else error "arity"
+apply1 p a = if fromMaybe 1 (arity p) == 1 then apply' p [a] else error "arity"
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then apply' p [a,b] else error "arity"
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then apply' p [a,b,c] else error "arity"
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then apply' p [a,b,c,d] else error "arity"
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then apply' p [a,b,c,d,e] else error "arity"
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then apply' p [a,b,c,d,e,f] else error "arity"
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then apply' p [a,b,c,d,e,f,g] else error "arity"
+
+showApply :: (Apply atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showApply =
+    foldApply (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+              (\ x -> if x then "true" else "false")
+
+prettyApply :: (Apply atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyApply pv pp pf prec atom =
+    foldApply (\ p ts ->
+                   pp p <> case ts of
+                             [] -> empty
+                             _ -> parens (cat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+              (\ x -> text (if x then "true" else "false"))
+              atom
+
+-- | Return the variables that occur in an instance of Apply.
+varApply :: (Apply atom p term, Term term v f) => atom -> Set.Set v
+varApply = foldApply (\ _ args -> Set.unions (map fvt args)) (const Set.empty)
+
+substApply :: (Apply atom p term, Constants atom, Term term v f) => Map.Map v term -> atom -> atom
+substApply env = foldApply (\ p args -> apply p (map (tsubst env) args)) fromBool
+
+{-
+instance (Apply atom p term, Term term v f, Constants atom) => Formula atom term v where
+    allVariables = varApply
+    freeVariables = varApply
+    substitute = substApply
+-}
diff --git a/testdata/logic/Data/Logic/Classes/Arity.hs b/testdata/logic/Data/Logic/Classes/Arity.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Arity.hs
@@ -0,0 +1,11 @@
+module Data.Logic.Classes.Arity
+    ( Arity(arity)
+    ) where
+
+-- |A class that characterizes how many arguments a predicate or
+-- function takes.  Depending on the context, a result of Nothing may
+-- mean that the arity is undetermined or unknown.  However, even if
+-- this returns Nothing, the same number of arguments must be passed
+-- to all uses of a given predicate or function.
+class Arity p where
+    arity :: p -> Maybe Int
diff --git a/testdata/logic/Data/Logic/Classes/Atom.hs b/testdata/logic/Data/Logic/Classes/Atom.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Atom.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, TypeFamilies #-}
+-- | Substitution and finding variables are two basic operations on
+-- formulas that contain terms and variables.  If a formula type
+-- supports quantifiers we can also find free variables, otherwise all
+-- variables are considered free.
+module Data.Logic.Classes.Atom
+    ( Atom(..)
+    -- , Formula(..)
+    ) where
+
+import Control.Applicative.Error (Failing)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+{-
+class Formula formula term v | formula -> term v where
+    substitute :: Map.Map v term -> formula -> formula
+    allVariables :: formula -> Set.Set v
+    freeVariables :: formula -> Set.Set v
+    unify :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    match :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> formula -> r
+    isRename :: formula -> formula -> Bool
+    getSubst :: Map.Map v term -> formula -> Map.Map v term
+-}
+
+class Atom atom term v | atom -> term v where
+    substitute :: Map.Map v term -> atom -> atom
+    allVariables :: atom -> Set.Set v
+    freeVariables :: atom -> Set.Set v
+    unify :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    match :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> atom -> r
+    isRename :: atom -> atom -> Bool
+    getSubst :: Map.Map v term -> atom -> Map.Map v term
+
diff --git a/testdata/logic/Data/Logic/Classes/ClauseNormalForm.hs b/testdata/logic/Data/Logic/Classes/ClauseNormalForm.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/ClauseNormalForm.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.ClauseNormalForm
+    ( ClauseNormalFormula(clauses, makeCNF, satisfiable)
+    ) where
+
+import Control.Monad (MonadPlus)
+import Data.Logic.Classes.Negate
+import Data.Set as S
+
+-- |A class to represent formulas in CNF, which is the conjunction of
+-- a set of disjuncted literals each which may or may not be negated.
+class (Negatable lit, Eq lit, Ord lit) => ClauseNormalFormula cnf lit | cnf -> lit where
+    clauses :: cnf -> S.Set (S.Set lit)
+    makeCNF :: S.Set (S.Set lit) -> cnf
+    satisfiable :: MonadPlus m => cnf -> m Bool
diff --git a/testdata/logic/Data/Logic/Classes/Combine.hs b/testdata/logic/Data/Logic/Classes/Combine.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Combine.hs
@@ -0,0 +1,123 @@
+-- | Class Logic defines the basic boolean logic operations,
+-- AND, OR, NOT, and so on.  Definitions which pertain to both
+-- propositional and first order logic are here.
+{-# LANGUAGE DeriveDataTypeable #-}
+module Data.Logic.Classes.Combine
+    ( Combinable(..)
+    , Combination(..)
+    , combine
+    , BinOp(..)
+    , binop
+    -- * Unicode aliases for Combinable class methods
+    , (∧)
+    , (∨)
+    , (⇒)
+    , (⇔)
+    -- * Use in Harrison's code
+    , (==>)
+    , (<=>)
+    , prettyBinOp
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Negate (Negatable, (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- | A type class for logical formulas.  Minimal implementation:
+-- @
+--  (.|.)
+-- @
+class (Negatable formula) => Combinable formula where
+    -- | Disjunction/OR
+    (.|.) :: formula -> formula -> formula
+
+    -- | Derived formula combinators.  These could (and should!) be
+    -- overridden with expressions native to the instance.
+    --
+    -- | Conjunction/AND
+    (.&.) :: formula -> formula -> formula
+    x .&. y = (.~.) ((.~.) x .|. (.~.) y)
+    -- | Formula combinators: Equivalence
+    (.<=>.) :: formula -> formula -> formula
+    x .<=>. y = (x .=>. y) .&. (y .=>. x)
+    -- | Implication
+    (.=>.) :: formula -> formula -> formula
+    x .=>. y = ((.~.) x .|. y)
+    -- | Reverse implication:
+    (.<=.) :: formula -> formula -> formula
+    x .<=. y = y .=>. x
+    -- | Exclusive or
+    (.<~>.) :: formula -> formula -> formula
+    x .<~>. y = ((.~.) x .&. y) .|. (x .&. (.~.) y)
+    -- | Nor
+    (.~|.) :: formula -> formula -> formula
+    x .~|. y = (.~.) (x .|. y)
+    -- | Nand
+    (.~&.) :: formula -> formula -> formula
+    x .~&. y = (.~.) (x .&. y)
+
+infixl 1  .<=>. ,  .<~>., ⇔, <=>
+infixr 2  .=>., ⇒, ==>
+infixr 3  .|., ∨
+infixl 4  .&., ∧
+
+-- |'Combination' is a helper type used in the signatures of the
+-- 'foldPropositional' and 'foldFirstOrder' methods so can represent
+-- all the ways that formulas can be combined using boolean logic -
+-- negation, logical And, and so forth.
+data Combination formula
+    = BinOp formula BinOp formula
+    | (:~:) formula
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | A helper function for building folds:
+-- @
+--   foldPropositional combine atomic
+-- @
+-- is a no-op.
+combine :: Combinable formula => Combination formula -> formula
+combine (BinOp f1 (:<=>:) f2) = f1 .<=>. f2
+combine (BinOp f1 (:=>:) f2) = f1 .=>. f2
+combine (BinOp f1 (:&:) f2) = f1 .&. f2
+combine (BinOp f1 (:|:) f2) = f1 .|. f2
+combine ((:~:) f) = (.~.) f
+
+-- | Represents the boolean logic binary operations, used in the
+-- Combination type above.
+data BinOp
+    = (:<=>:)  -- ^ Equivalence
+    |  (:=>:)  -- ^ Implication
+    |  (:&:)  -- ^ AND
+    |  (:|:)  -- ^ OR
+    deriving (Eq, Ord, Data, Typeable, Enum, Bounded, Show, Read)
+
+binop :: Combinable formula => formula -> BinOp -> formula -> formula
+binop a (:&:) b = a .&. b
+binop a (:|:) b = a .|. b
+binop a (:=>:) b = a .=>. b
+binop a (:<=>:) b = a .<=>. b
+
+(∧) :: Combinable formula => formula -> formula -> formula
+(∧) = (.&.)
+(∨) :: Combinable formula => formula -> formula -> formula
+(∨) = (.|.)
+-- | ⇒ can't be a function when -XUnicodeSyntax is enabled.
+(⇒) :: Combinable formula => formula -> formula -> formula
+(⇒) = (.=>.)
+(⇔) :: Combinable formula => formula -> formula -> formula
+(⇔) = (.<=>.)
+
+(==>) :: Combinable formula => formula -> formula -> formula
+(==>) = (.=>.)
+(<=>) :: Combinable formula => formula -> formula -> formula
+(<=>) = (.<=>.)
+
+prettyBinOp :: BinOp -> Doc
+prettyBinOp (:<=>:) = text "⇔"
+prettyBinOp (:=>:) = text "⇒"
+prettyBinOp (:&:) = text "∧"
+prettyBinOp (:|:) = text "∨"
+
+instance Pretty BinOp where
+    pretty = prettyBinOp
diff --git a/testdata/logic/Data/Logic/Classes/Constants.hs b/testdata/logic/Data/Logic/Classes/Constants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Constants.hs
@@ -0,0 +1,40 @@
+module Data.Logic.Classes.Constants
+    ( Constants(asBool, fromBool)
+    , ifElse
+    , true
+    , (⊨)
+    , false
+    , (⊭)
+    , prettyBool
+    ) where
+
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- |Some types in the Logic class heirarchy need to have True and
+-- False elements.
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+true :: Constants p => p
+true = fromBool True
+
+false :: Constants p => p
+false = fromBool False
+
+ifElse :: a -> a -> Bool -> a
+ifElse t _ True = t
+ifElse _ f False = f
+
+(⊨) :: Constants formula => formula
+(⊨) = true
+(⊭) :: Constants formula => formula
+(⊭) = false
+
+prettyBool :: Bool -> Doc
+prettyBool True = text "⊨"
+prettyBool False = text "⊭"
+
+instance Pretty Bool where
+    pretty = prettyBool
diff --git a/testdata/logic/Data/Logic/Classes/Equals.hs b/testdata/logic/Data/Logic/Classes/Equals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Equals.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+-- | Support for equality.
+module Data.Logic.Classes.Equals
+    ( AtomEq(..)
+    , applyEq
+    , PredicateName(..)
+    , zipAtomsEq
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , pApp, pApp0, pApp1, pApp2, pApp3, pApp4, pApp5, pApp6, pApp7
+    , showFirstOrderFormulaEq
+    , (.=.), (≡)
+    , (.!=.), (≢)
+    , fromAtomEq
+    , showAtomEq
+    , prettyAtomEq
+    , varAtomEq
+    , substAtomEq
+    , funcsAtomEq
+    ) where
+
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Data.Logic.Classes.Term (Term, convertTerm, showTerm, prettyTerm, fvt, tsubst, funcs)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), (<+>), text, empty, parens, hcat, nest)
+
+-- | Its not safe to make Atom a superclass of AtomEq, because the Atom methods will fail on AtomEq instances.
+class Predicate p => AtomEq atom p term | atom -> p term, term -> atom p where
+    foldAtomEq :: (p -> [term] -> r) -> (Bool -> r) -> (term -> term -> r) -> atom -> r
+    equals :: term -> term -> atom
+    applyEq' :: p -> [term] -> atom
+
+-- | applyEq' with an arity check - clients should always call this.
+applyEq :: AtomEq atom p term => p -> [term] -> atom
+applyEq p ts =
+    case arity p of
+      Just n | n /= length ts -> arityError p ts
+      _ -> applyEq' p ts
+
+-- | A way to represent any predicate's name.  Frequently the equality
+-- predicate has no standalone representation in the p type, it is
+-- just a constructor in the atom type, or even the formula type.
+data PredicateName p = Named p Int | Equals deriving (Eq, Ord, Show)
+
+instance (Pretty p, Ord p) => Pretty (PredicateName p) where
+    pretty Equals = text "="
+    pretty (Named p _) = pretty p
+
+zipAtomsEq :: AtomEq atom p term =>
+              (p -> [term] -> p -> [term] -> Maybe r)
+           -> (Bool -> Bool -> Maybe r)
+           -> (term -> term -> term -> term -> Maybe r)
+           -> atom -> atom -> Maybe r
+zipAtomsEq ap tf eq a1 a2 =
+    foldAtomEq ap' tf' eq' a1
+    where
+      ap' p1 ts1 = foldAtomEq (ap p1 ts1) (\ _ -> Nothing) (\ _ _ -> Nothing) a2
+      tf' x1 = foldAtomEq (\ _ _ -> Nothing) (tf x1) (\ _ _ -> Nothing) a2
+      eq' t1 t2 = foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) (eq t1 t2) a2
+
+apply0 :: AtomEq atom p term => p -> atom
+apply0 p = if fromMaybe 0 (arity p) == 0 then applyEq' p [] else arityError p []
+apply1 :: AtomEq atom p a => p -> a -> atom
+apply1 p a = if fromMaybe 1 (arity p) == 1 then applyEq' p [a] else arityError p [a]
+apply2 :: AtomEq atom p a => p -> a -> a -> atom
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then applyEq' p [a,b] else arityError p [a,b]
+apply3 :: AtomEq atom p a => p -> a -> a -> a -> atom
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then applyEq' p [a,b,c] else arityError p [a,b,c]
+apply4 :: AtomEq atom p a => p -> a -> a -> a -> a -> atom
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then applyEq' p [a,b,c,d] else arityError p [a,b,c,d]
+apply5 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> atom
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then applyEq' p [a,b,c,d,e] else arityError p [a,b,c,d,e]
+apply6 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> atom
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then applyEq' p [a,b,c,d,e,f] else arityError p [a,b,c,d,e,f]
+apply7 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> a -> atom
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then applyEq' p [a,b,c,d,e,f,g] else arityError p [a,b,c,d,e,f,g]
+
+arityError :: (Arity p) => p -> [a] -> t
+arityError _p _ts = error "arity error"
+-- arityError :: (Arity p, Pretty p) => p -> [a] -> t
+-- arityError p ts = error $ "arity error: " ++ show (length ts) ++ " arguments applied to arity " ++ show (arity p) ++ " predicate " ++ show (pretty p)
+
+pApp :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> [term] -> formula
+pApp p ts = atomic (applyEq p ts)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a)
+pApp2 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b)
+pApp3 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c)
+pApp4 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d)
+pApp5 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e)
+pApp6 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f)
+pApp7 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g)
+
+showFirstOrderFormulaEq :: (FirstOrderFormula fof atom v, AtomEq atom p term, Show term, Show v, Show p) => fof -> String
+showFirstOrderFormulaEq fm =
+    fst (sfo fm)
+    where
+      sfo p = foldFirstOrder qu co tf pr p
+      qu op v f = (showQuant op ++ " " ++ show v ++ " " ++ parens quantPrec (sfo f), quantPrec)
+      co ((:~:) p) =
+          let prec' = 5 in
+          ("(.~.)" ++ parens prec' (sfo p), prec')
+      co (BinOp p op q) = (parens (opPrec op) (sfo p) ++ " " ++ showBinOp op ++ " " ++ parens (opPrec op) (sfo q), opPrec op)
+      tf x = (if x then "true" else "false", 0)
+      pr = foldAtomEq (\ p ts -> ("pApp " ++ show p ++ " " ++ show ts, 6))
+                      (\ x -> (if x then "true" else "false", 0))
+                      (\ t1 t2 -> ("(" ++ show t1 ++ ") .=. (" ++ show t2 ++ ")", 6))
+      showBinOp (:<=>:) = ".<=>."
+      showBinOp (:=>:) = ".=>."
+      showBinOp (:&:) = ".&."
+      showBinOp (:|:) = ".|."
+      showQuant Exists = "exists"
+      showQuant Forall = "for_all"
+      opPrec (:|:) = 3
+      opPrec (:&:) = 4
+      opPrec (:=>:) = 2
+      opPrec (:<=>:) = 2
+      quantPrec = 1
+      parens :: Int -> (String, Int) -> String
+      parens prec' (s, prec) = if prec >= prec' then "(" ++ s ++ ")" else s
+
+infix 5 .=., .!=., ≡, ≢
+
+(.=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .=. b = atomic (equals a b)
+
+(.!=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .!=. b = (.~.) (a .=. b)
+
+(≡) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≡) = (.=.)
+
+(≢) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≢) = (.!=.)
+
+{-
+instance (AtomEq atom p term, Constants atom, Variable v, Term term v f) => Formula atom term v where
+    substitute env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+    allVariables = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+    freeVariables = allVariables
+-}
+
+fromAtomEq :: (AtomEq atom1 p1 term1, Term term1 v1 f1,
+               AtomEq atom2 p2 term2, Term term2 v2 f2, Constants atom2) =>
+              (v1 -> v2) -> (p1 -> p2) -> (f1 -> f2) -> atom1 -> atom2
+fromAtomEq cv cp cf atom =
+    foldAtomEq (\ pr ts -> applyEq (cp pr) (map ct ts))
+               fromBool
+               (\ a b -> ct a `equals` ct b)
+               atom
+    where
+      ct = convertTerm cv cf
+
+showAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showAtomEq =
+    foldAtomEq (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+               (\ x -> if x then "true" else "false")
+               (\ t1 t2 -> "(" ++ parenTerm t1 ++ " .=. " ++ parenTerm t2 ++ ")")
+    where
+      parenTerm :: term -> String
+      parenTerm x = "(" ++ showTerm x ++ ")"
+
+prettyAtomEq :: (AtomEq atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyAtomEq pv pp pf prec atom =
+    foldAtomEq (\ p ts -> pp p <> case ts of
+                                    [] -> empty
+                                    _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+               (text . ifElse "true" "false")
+               (\ t1 t2 -> parensIf (prec > 6) (prettyTerm pv pf t1 <+> text "=" <+> prettyTerm pv pf t2))
+               atom
+    where
+      parensIf False = id
+      parensIf _ = parens . nest 1
+
+-- | Return the variables that occur in an instance of AtomEq.
+varAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f) => atom -> Set.Set v
+varAtomEq = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+
+substAtomEq :: (AtomEq atom p term, Constants atom, Term term v f) =>
+               Map.Map v term -> atom -> atom
+substAtomEq env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+
+funcsAtomEq :: (AtomEq atom p term, Term term v f, Ord f) => atom -> Set.Set (f, Int)
+funcsAtomEq = foldAtomEq (\ _ ts -> Set.unions (map funcs ts)) (const Set.empty) (\ t1 t2 -> Set.union (funcs t1) (funcs t2))
diff --git a/testdata/logic/Data/Logic/Classes/FirstOrder.hs b/testdata/logic/Data/Logic/Classes/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/FirstOrder.hs
@@ -0,0 +1,303 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+module Data.Logic.Classes.FirstOrder
+    ( FirstOrderFormula(..)
+    , Quant(..)
+    , zipFirstOrder
+    , pApp
+    , pApp0
+    , pApp1
+    , pApp2
+    , pApp3
+    , pApp4
+    , pApp5
+    , pApp6
+    , pApp7
+    , for_all'
+    , exists'
+    , quant
+    , (!)
+    , (?)
+    , (∀)
+    , (∃)
+    , quant'
+    , convertFOF
+    , toPropositional
+    , withUnivQuants
+    , showFirstOrder
+    , prettyFirstOrder
+    , fixityFirstOrder
+    , foldAtomsFirstOrder
+    , mapAtomsFirstOrder
+    , onatoms
+    , overatoms
+    , atom_union
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), apply, apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7)
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import qualified Data.Logic.Classes.Propositional as P
+import Data.Logic.Classes.Variable (Variable)
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), (<+>), text, parens, nest)
+
+-- |The 'FirstOrderFormula' type class.  Minimal implementation:
+-- @for_all, exists, foldFirstOrder, foldTerm, (.=.), pApp0-pApp7, fApp, var@.  The
+-- functional dependencies are necessary here so we can write
+-- functions that don't fix all of the type parameters.  For example,
+-- without them the univquant_free_vars function gives the error @No
+-- instance for (FirstOrderFormula Formula atom V)@ because the
+-- function doesn't mention the Term type.
+class ( Formula formula atom
+      , Combinable formula  -- Basic logic operations
+      , Constants formula
+      , Constants atom
+      , HasFixity atom
+      , Variable v
+      , Pretty atom, Pretty v
+      ) => FirstOrderFormula formula atom v | formula -> atom v where
+    -- | Universal quantification - for all x (formula x)
+    for_all :: v -> formula -> formula
+    -- | Existential quantification - there exists x such that (formula x)
+    exists ::  v -> formula -> formula
+
+    -- | A fold function similar to the one in 'PropositionalFormula'
+    -- but extended to cover both the existing formula types and the
+    -- ones introduced here.  @foldFirstOrder (.~.) quant binOp infixPred pApp@
+    -- is a no op.  The argument order is taken from Logic-TPTP.
+    foldFirstOrder :: (Quant -> v -> formula -> r)
+                   -> (Combination formula -> r)
+                   -> (Bool -> r)
+                   -> (atom -> r)
+                   -> formula
+                   -> r
+
+zipFirstOrder :: FirstOrderFormula formula atom v =>
+                 (Quant -> v -> formula -> Quant -> v -> formula -> Maybe r)
+              -> (Combination formula -> Combination formula -> Maybe r)
+              -> (Bool -> Bool -> Maybe r)
+              -> (atom -> atom -> Maybe r)
+              -> formula -> formula -> Maybe r
+zipFirstOrder qu co tf at fm1 fm2 =
+    foldFirstOrder qu' co' tf' at' fm1
+    where
+      qu' op1 v1 p1 = foldFirstOrder (qu op1 v1 p1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      co' c1 = foldFirstOrder (\ _ _ _ -> Nothing) (co c1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' atom1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at atom1) fm2
+
+-- |The 'Quant' and 'InfixPred' types, like the BinOp type in
+-- 'Data.Logic.Propositional', could be additional parameters to the type
+-- class, but it would add additional complexity with unclear
+-- benefits.
+data Quant = Forall | Exists deriving (Eq,Ord,Show,Read,Data,Typeable,Enum,Bounded)
+
+pApp :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> [term] -> formula
+pApp p ts = atomic (apply p ts :: atom)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a :: atom)
+pApp2 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b :: atom)
+pApp3 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c :: atom)
+pApp4 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d :: atom)
+pApp5 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e :: atom)
+pApp6 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f :: atom)
+pApp7 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g :: atom)
+
+-- |for_all with a list of variables, for backwards compatibility.
+for_all' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+for_all' vs f = foldr for_all f vs
+
+-- |exists with a list of variables, for backwards compatibility.
+exists' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+exists' vs f = foldr for_all f vs
+
+-- |Names for for_all and exists inspired by the conventions of the
+-- TPTP project.
+(!) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(!) = for_all
+(?) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(?) = exists
+
+-- Irrelevant, because these are always used as prefix operators, never as infix.
+infixr 9 !, ?, ∀, ∃
+
+-- | ∀ can't be a function when -XUnicodeSyntax is enabled.
+(∀) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∀) = for_all
+(∃) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∃) = exists
+
+-- | Helper function for building folds.
+quant :: FirstOrderFormula formula atom v => 
+         Quant -> v -> formula -> formula
+quant Forall v f = for_all v f
+quant Exists v f = exists v f
+
+-- |Legacy version of quant from when we supported lists of quantified
+-- variables.  It also has the virtue of eliding quantifications with
+-- empty variable lists (by calling for_all' and exists'.)
+quant' :: FirstOrderFormula formula atom v => 
+         Quant -> [v] -> formula -> formula
+quant' Forall = for_all'
+quant' Exists = exists'
+
+convertFOF :: (FirstOrderFormula formula1 atom1 v1, FirstOrderFormula formula2 atom2 v2) =>
+              (atom1 -> atom2) -> (v1 -> v2) -> formula1 -> formula2
+convertFOF convertA convertV formula =
+    foldFirstOrder qu co tf (atomic . convertA) formula
+    where
+      convert' = convertFOF convertA convertV
+      qu x v f = quant x (convertV v) (convert' f)
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+
+-- |Try to convert a first order logic formula to propositional.  This
+-- will return Nothing if there are any quantifiers, or if it runs
+-- into an atom that it is unable to convert.
+toPropositional :: forall formula1 atom v formula2 atom2.
+                   (FirstOrderFormula formula1 atom v,
+                    P.PropositionalFormula formula2 atom2) =>
+                   (atom -> atom2) -> formula1 -> formula2
+toPropositional convertAtom formula =
+    foldFirstOrder qu co tf at formula
+    where
+      convert' = toPropositional convertAtom
+      qu _ _ _ = error "toPropositional: invalid argument"
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+      at = atomic . convertAtom
+
+-- | Display a formula in a format that can be read into the interpreter.
+showFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v, Show v) => (atom -> String) -> formula -> String
+showFirstOrder sa formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu Forall v f = "(for_all " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
+      qu Exists v f = "(exists " ++  show v ++ " " ++ showFirstOrder sa f ++ ")"
+      co (BinOp f1 op f2) = "(" ++ parenForm f1 ++ " " ++ showCombine op ++ " " ++ parenForm f2 ++ ")"
+      co ((:~:) f) = "((.~.) " ++ showFirstOrder sa f ++ ")"
+      tf x = if x then "true" else "false"
+      at :: atom -> String
+      at = sa
+      parenForm x = "(" ++ showFirstOrder sa x ++ ")"
+      showCombine (:<=>:) = ".<=>."
+      showCombine (:=>:) = ".=>."
+      showCombine (:&:) = ".&."
+      showCombine (:|:) = ".|."
+
+prettyFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v) =>
+                      (Int -> atom -> Doc) -> (v -> Doc) -> Int -> formula -> Doc
+prettyFirstOrder pa pv pprec formula =
+    parensIf (pprec > prec) $
+    foldFirstOrder
+          (\ qop v f -> prettyQuant qop <> pv v <> text "." <+> (prettyFirstOrder pa pv prec f))
+          (\ cm ->
+               case cm of
+                 (BinOp f1 op f2) ->
+                     case op of
+                       (:=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:<=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:&:) -> (prettyFirstOrder pa pv 3 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:|:) -> (prettyFirstOrder pa pv 4 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                 ((:~:) f) -> text "¬" {-"~"-} <> prettyFirstOrder pa pv prec f)
+          (text . ifElse "true" "false")
+          (pa prec)
+          formula
+    where
+      Fixity prec _ = fixityFirstOrder formula
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      prettyQuant Forall = text "∀" -- "!"
+      prettyQuant Exists = text "∃" -- "?"
+
+fixityFirstOrder :: (HasFixity atom, FirstOrderFormula formula atom v) => formula -> Fixity
+fixityFirstOrder formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu _ _ _ = Fixity 10 InfixN
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- | Examine the formula to find the list of outermost universally
+-- quantified variables, and call a function with that list and the
+-- formula after the quantifiers are removed.
+withUnivQuants :: FirstOrderFormula formula atom v => ([v] -> formula -> r) -> formula -> r
+withUnivQuants fn formula =
+    doFormula [] formula
+    where
+      doFormula vs f =
+          foldFirstOrder
+                (doQuant vs)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                f
+      doQuant vs Forall v f = doFormula (v : vs) f
+      doQuant vs Exists v f = fn (reverse vs) (exists v f)
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+mapAtomsFirstOrder :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+mapAtomsFirstOrder f fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op v p = quant op v (mapAtomsFirstOrder f p)
+      co ((:~:) p) = mapAtomsFirstOrder f p
+      co (BinOp p op q) = binop (mapAtomsFirstOrder f p) op (mapAtomsFirstOrder f q)
+      tf flag = fromBool flag
+      at x = f x
+
+-- | Deprecated - use mapAtoms
+onatoms :: forall formula atom v. (FirstOrderFormula formula atom v) => (atom -> formula) -> formula -> formula
+onatoms = mapAtomsFirstOrder
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- -------------------------------------------------------------------------
+
+foldAtomsFirstOrder :: FirstOrderFormula fof atom v => (r -> atom -> r) -> r -> fof -> r
+foldAtomsFirstOrder f i fof =
+        foldFirstOrder qu co (const i) (f i) fof
+        where
+          qu _ _ fof' = foldAtomsFirstOrder f i fof'
+          co ((:~:) fof') = foldAtomsFirstOrder f i fof'
+          co (BinOp p _ q) = foldAtomsFirstOrder f (foldAtomsFirstOrder f i q) p
+
+-- | Deprecated - use foldAtoms
+overatoms :: forall formula atom v r. FirstOrderFormula formula atom v =>
+             (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsFirstOrder (flip f) b fm
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: forall formula atom v a. (FirstOrderFormula formula atom v, Ord a) =>
+              (atom -> Set.Set a) -> formula -> Set.Set a
+atom_union f fm = overatoms (\ h t -> Set.union (f h) t) fm Set.empty
+
+$(deriveSafeCopy 1 'base ''Quant)
diff --git a/testdata/logic/Data/Logic/Classes/Formula.hs b/testdata/logic/Data/Logic/Classes/Formula.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Formula.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Data.Logic.Classes.Formula
+    ( Formula(atomic, foldAtoms, mapAtoms)
+    ) where
+
+class Formula formula atom where
+    atomic :: atom -> formula
+    foldAtoms :: Formula formula atom => (r -> atom -> r) -> r -> formula -> r
+    mapAtoms :: Formula formula atom => (atom -> formula) -> formula -> formula
diff --git a/testdata/logic/Data/Logic/Classes/Literal.hs b/testdata/logic/Data/Logic/Classes/Literal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Literal.hs
@@ -0,0 +1,120 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal
+    ( Literal(..)
+    , zipLiterals
+    , fromFirstOrder
+    , fromLiteral
+    , toPropositional
+    , prettyLit
+    , foldAtomsLiteral
+    ) where
+
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Constants
+import qualified Data.Logic.Classes.FirstOrder as FOF
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Pretty (HasFixity(..), Fixity(..), FixityDirection(..))
+import qualified Data.Logic.Classes.Propositional as P
+import Data.Logic.Classes.Negate
+import Data.Logic.Failing (Failing(..))
+import Text.PrettyPrint (Doc, (<>), text, parens, nest)
+
+-- |Literals are the building blocks of the clause and implicative normal
+-- |forms.  They support negation and must include True and False elements.
+class (Negatable lit, Constants lit, HasFixity atom, Formula lit atom, Ord lit) => Literal lit atom | lit -> atom where
+    foldLiteral :: (lit -> r) -> (Bool -> r) -> (atom -> r) -> lit -> r
+
+zipLiterals :: Literal lit atom =>
+               (lit -> lit -> Maybe r)
+            -> (Bool -> Bool -> Maybe r)
+            -> (atom -> atom -> Maybe r)
+            -> lit -> lit -> Maybe r
+zipLiterals neg tf at fm1 fm2 =
+    foldLiteral neg' tf' at' fm1
+    where
+      neg' p1 = foldLiteral (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldLiteral (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' a1 = foldLiteral (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
+
+{- This makes bad things happen.
+-- | We can use an fof type as a lit, but it must not use some constructs.
+instance FirstOrderFormula fof atom v => Literal fof atom v where
+    foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm
+        where qu = error "instance Literal FirstOrderFormula"
+              co ((:~:) x) = neg x
+              co _ = error "instance Literal FirstOrderFormula"
+    atomic = Data.Logic.Classes.FirstOrder.atomic
+-}
+
+-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
+-- with a construct unsupported in a normal logic formula,
+-- i.e. quantifiers and formula combinators other than negation.
+fromFirstOrder :: forall formula atom v lit atom2.
+                  (Formula lit atom2, FOF.FirstOrderFormula formula atom v, Literal lit atom2) =>
+                  (atom -> atom2) -> formula -> Failing lit
+fromFirstOrder ca formula =
+    FOF.foldFirstOrder (\ _ _ _ -> Failure ["fromFirstOrder"]) co (Success . fromBool) (Success . atomic . ca) formula
+    where
+      co :: Combination formula -> Failing lit
+      co ((:~:) f) =  fromFirstOrder ca f >>= return . (.~.)
+      co _ = Failure ["fromFirstOrder"]
+
+fromLiteral :: forall lit atom v fof atom2. (Literal lit atom, FOF.FirstOrderFormula fof atom2 v) =>
+               (atom -> atom2) -> lit -> fof
+fromLiteral ca lit = foldLiteral (\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit
+
+toPropositional :: forall lit atom pf atom2. (Literal lit atom, P.PropositionalFormula pf atom2) =>
+                   (atom -> atom2) -> lit -> pf
+toPropositional ca lit = foldLiteral (\ p -> (.~.) (toPropositional ca p)) fromBool (atomic . ca) lit
+
+{-
+prettyLit :: forall lit atom term v p f. (Literal lit atom v, Apply atom p term, Term term v f) =>
+              (v -> Doc)
+           -> (p -> Doc)
+           -> (f -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pv pp pf _prec lit =
+    foldLiteral neg tf at lit
+    where
+      neg :: lit -> Doc
+      neg x = if negated x then text {-"¬"-} "~" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x
+      tf = text . ifElse "true" "false"
+      at = foldApply (\ pr ts -> 
+                        pp pr <> case ts of
+                                   [] -> empty
+                                   _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+                   (\ x -> text $ if x then "true" else "false")
+      -- parensIf False = id
+      -- parensIf _ = parens . nest 1
+-}
+
+prettyLit :: forall lit atom v. (Literal lit atom) =>
+              (Int -> atom -> Doc)
+           -> (v -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pa pv pprec lit =
+    parensIf (pprec > prec) $ foldLiteral co tf at lit
+    where
+      co :: lit -> Doc
+      co x = if negated x then text {-"¬"-} "~" <> prettyLit pa pv 5 x else prettyLit pa pv 5 x
+      tf x = text (if x then "true" else "false")
+      at = pa 6
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      Fixity prec _ = fixityLiteral lit
+
+fixityLiteral :: (Literal formula atom) => formula -> Fixity
+fixityLiteral formula =
+    foldLiteral neg tf at formula
+    where
+      neg _ = Fixity 5 InfixN
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+foldAtomsLiteral :: Literal lit atom => (r -> atom -> r) -> r -> lit -> r
+foldAtomsLiteral f i lit = foldLiteral (foldAtomsLiteral f i) (const i) (f i) lit
diff --git a/testdata/logic/Data/Logic/Classes/Negate.hs b/testdata/logic/Data/Logic/Classes/Negate.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Negate.hs
@@ -0,0 +1,42 @@
+module Data.Logic.Classes.Negate
+     ( Negatable(..)
+     , negated
+     , (.~.)
+     , (¬)
+     , negative
+     , positive
+     ) where
+
+-- |The class of formulas that can be negated.  There are some types
+-- that can be negated but do not support the other Boolean Logic
+-- operators, such as the 'Literal' class.
+class Negatable formula where
+    -- | Negate a formula in a naive fashion, the operators below
+    -- prevent double negation.
+    negatePrivate :: formula -> formula
+    -- | Test whether a formula is negated or normal
+    foldNegation :: (formula -> r) -- ^ called for normal formulas
+                 -> (formula -> r) -- ^ called for negated formulas
+                 -> formula -> r
+-- | Is this formula negated at the top level?
+negated :: Negatable formula => formula -> Bool
+negated = foldNegation (const False) (not . negated)
+
+-- | Negate the formula, avoiding double negation
+(.~.) :: Negatable formula => formula -> formula
+(.~.) = foldNegation negatePrivate id
+
+(¬) :: Negatable formula => formula -> formula
+(¬) = (.~.)
+
+infix 5 .~., ¬
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.  (These names are used in Harrison's code.)
+-- ------------------------------------------------------------------------- 
+
+negative :: Negatable formula => formula -> Bool
+negative = negated
+
+positive :: Negatable formula => formula -> Bool
+positive = not . negative
diff --git a/testdata/logic/Data/Logic/Classes/Pretty.hs b/testdata/logic/Data/Logic/Classes/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Pretty.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Classes.Pretty
+    ( Pretty(pretty)
+    , HasFixity(fixity)
+    , TH.Fixity(..)
+    , TH.FixityDirection(..)
+    , topFixity
+    , botFixity
+    ) where
+
+import qualified Language.Haskell.TH.Syntax as TH
+import Text.PrettyPrint (Doc, text)
+
+-- | The intent of this class is to be similar to Show, but only one
+-- way, with no corresponding Read class.  It doesn't really belong
+-- here in logic-classes.  To put something in a pretty printing class
+-- implies that there is only one way to pretty print it, which is not
+-- an assumption made by Text.PrettyPrint.  But in practice this is
+-- often good enough.
+class Pretty x where
+    pretty :: x -> Doc
+
+-- | A class used to do proper parenthesization of formulas.  If we
+-- nest a higher precedence formula inside a lower one parentheses can
+-- be omitted.  Because @|@ has lower precedence than @&@, the formula
+-- @a | (b & c)@ appears as @a | b & c@, while @(a | b) & c@ appears
+-- unchanged.  (Name Precedence chosen because Fixity was taken.)
+-- 
+-- The second field of Fixity is the FixityDirection, which can be
+-- left, right, or non.  A left associative operator like @/@ is
+-- grouped left to right, so parenthese can be omitted from @(a / b) /
+-- c@ but not from @a / (b / c)@.  It is a syntax error to omit
+-- parentheses when formatting a non-associative operator.
+-- 
+-- The Haskell FixityDirection type is concerned with how to interpret
+-- a formula formatted in a certain way, but here we are concerned
+-- with how to format a formula given its interpretation.  As such,
+-- one case the Haskell type does not capture is whether the operator
+-- follows the associative law, so we can omit parentheses in an
+-- expression such as @a & b & c@.
+class HasFixity x where
+    fixity :: x -> TH.Fixity
+
+-- Definitions from template-haskell:
+-- data Fixity = Fixity Int FixityDirection
+-- data FixityDirection = InfixL | InfixR | InfixN
+
+-- | This is used as the initial value for the parent fixity.
+topFixity :: TH.Fixity
+topFixity = TH.Fixity 0 TH.InfixN
+
+-- | This is used as the fixity for things that never need
+-- parenthesization, such as function application.
+botFixity :: TH.Fixity
+botFixity = TH.Fixity 10 TH.InfixN
+
+instance Pretty String where
+    pretty = text
diff --git a/testdata/logic/Data/Logic/Classes/Propositional.hs b/testdata/logic/Data/Logic/Classes/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Propositional.hs
@@ -0,0 +1,333 @@
+-- | PropositionalFormula is a multi-parameter type class for
+-- representing instance of propositional (aka zeroth order) logic
+-- datatypes.  These are formulas which have truth values, but no "for
+-- all" or "there exists" quantifiers and thus no variables or terms
+-- as we have in first order or predicate logic.  It is intended that
+-- we will be able to write instances for various different
+-- implementations to allow these systems to interoperate.  The
+-- operator names were adopted from the Logic-TPTP package.
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Classes.Propositional
+    ( PropositionalFormula(..)
+    , showPropositional
+    , prettyPropositional
+    , fixityPropositional
+    , convertProp
+    , combine
+    , negationNormalForm
+    , clauseNormalForm
+    , clauseNormalForm'
+    , clauseNormalFormAlt
+    , clauseNormalFormAlt'
+    , disjunctiveNormalForm
+    , disjunctiveNormalForm'
+    , overatoms
+    , foldAtomsPropositional
+    , mapAtomsPropositional
+    ) where
+
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool, prettyBool)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate
+import Data.Logic.Classes.Pretty (Pretty, HasFixity(fixity), Fixity(Fixity), FixityDirection(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as Set
+import Text.PrettyPrint (Doc, text, (<>))
+
+-- |A type class for propositional logic.  If the type we are writing
+-- an instance for is a zero-order (aka propositional) logic type
+-- there will generally by a type or a type parameter corresponding to
+-- atom.  For first order or predicate logic types, it is generally
+-- easiest to just use the formula type itself as the atom type, and
+-- raise errors in the implementation if a non-atomic formula somehow
+-- appears where an atomic formula is expected (i.e. as an argument to
+-- atomic or to the third argument of foldPropositional.)
+-- 
+-- The Ord superclass is required so we can put formulas in sets
+-- during the normal form computations.  Negatable and Combinable are
+-- also considered basic operations that we can't build this package
+-- without.  It is less obvious whether Constants is always required,
+-- but the implementation of functions like simplify would be more
+-- elaborate if we didn't have it, so we will require it.
+class (Ord formula, Negatable formula, Combinable formula, Constants formula,
+       Pretty formula, HasFixity formula, Formula formula atom) => PropositionalFormula formula atom | formula -> atom where
+    -- | Build an atomic formula from the atom type.
+    -- | A fold function that distributes different sorts of formula
+    -- to its parameter functions, one to handle binary operators, one
+    -- for negations, and one for atomic formulas.  See examples of its
+    -- use to implement the polymorphic functions below.
+    foldPropositional :: (Combination formula -> r)
+                      -> (Bool -> r)
+                      -> (atom -> r)
+                      -> formula -> r
+
+-- | Show a formula in a format that can be evaluated 
+showPropositional :: (PropositionalFormula formula atom) => (atom -> String) -> formula -> String
+showPropositional showAtom formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) f) = "(.~.) " ++ parenForm f
+      co (BinOp f1 op f2) = parenForm f1 ++ " " ++ showFormOp op ++ " " ++ parenForm f2
+      tf True = "true"
+      tf False = "false"
+      at = showAtom
+      parenForm x = "(" ++ showPropositional showAtom x ++ ")"
+      showFormOp (:<=>:) = ".<=>."
+      showFormOp (:=>:) = ".=>."
+      showFormOp (:&:) = ".&."
+      showFormOp (:|:) = ".|."
+
+-- | Show a formula in a visually pleasing format.
+prettyPropositional :: (PropositionalFormula formula atom, HasFixity formula) =>
+                       (atom -> Doc)
+                    -> Fixity        -- ^ The fixity of the parent formula.  If the operator being formatted here
+                                     -- has a lower precedence it needs to be parenthesized.
+                    -> formula
+                    -> Doc
+prettyPropositional prettyAtom (Fixity pprec _pdir) formula =
+    parenIf (pprec > prec) (foldPropositional co tf at formula)
+    where
+      co ((:~:) f) = text "¬" <> prettyPropositional prettyAtom fix f
+      co (BinOp f1 op f2) = prettyPropositional prettyAtom fix f1 <> text " " <> prettyBinOp op <> text " " <> prettyPropositional prettyAtom fix f2
+      tf = prettyBool
+      at = prettyAtom
+      -- parenForm x = cat [text "(", prettyPropositional prettyAtom 0 x, text ")"]
+      parenIf True x = text "(" <> x <> text ")"
+      parenIf False x = x
+      fix@(Fixity prec _dir) = fixity formula
+
+fixityPropositional :: (HasFixity atom, PropositionalFormula formula atom) => formula -> Fixity
+fixityPropositional formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- |Convert any instance of a propositional logic expression to any
+-- other using the supplied atom conversion function.
+convertProp :: forall formula1 atom1 formula2 atom2.
+               (PropositionalFormula formula1 atom1,
+                PropositionalFormula formula2 atom2) =>
+               (atom1 -> atom2) -> formula1 -> formula2
+convertProp convertA formula =
+    foldPropositional c fromBool a formula
+    where
+      convert' = convertProp convertA
+      c ((:~:) f) = (.~.) (convert' f)
+      c (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      a = atomic . convertA
+
+-- | Simplify and recursively apply nnf.
+negationNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+negationNormalForm = nnf . psimplify
+
+-- |Eliminate => and <=> and move negations inwards:
+-- 
+-- @
+-- Formula      Rewrites to
+--  P => Q      ~P | Q
+--  P <=> Q     (P & Q) | (~P & ~Q)
+-- ~∀X P        ∃X ~P
+-- ~∃X P        ∀X ~P
+-- ~(P & Q)     (~P | ~Q)
+-- ~(P | Q)     (~P & ~Q)
+-- ~~P  P
+-- @
+-- 
+nnf :: (PropositionalFormula formula atom) => formula -> formula
+nnf fm = foldPropositional (nnfCombine fm) fromBool (\ _ -> fm) fm
+
+nnfCombine :: (PropositionalFormula formula atom) => formula -> Combination formula -> formula
+nnfCombine fm ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+nnfCombine _ (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+nnfCombine _ (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+nnfCombine _ (BinOp p (:&:) q) = nnf p .&. nnf q
+nnfCombine _ (BinOp p (:|:) q) = nnf p .|. nnf q
+
+nnfNotCombine :: (PropositionalFormula formula atom) => Combination formula -> formula
+nnfNotCombine ((:~:) p) = nnf p
+nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- |Do a bottom-up recursion to simplify a propositional formula.
+psimplify :: (PropositionalFormula formula atom) => formula -> formula
+psimplify fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      co (BinOp p (:&:) q) = psimplify1 (psimplify p .&. psimplify q)
+      co (BinOp p (:|:) q) = psimplify1 (psimplify p .|. psimplify q)
+      co (BinOp p (:=>:) q) = psimplify1 (psimplify p .=>. psimplify q)
+      co (BinOp p (:<=>:) q) = psimplify1 (psimplify p .<=>. psimplify q)
+      tf _ = fm
+      at _ = fm
+
+-- |Do one step of simplify for propositional formulas:
+-- Perform the following transformations everywhere, plus any
+-- commuted versions for &, |, and <=>.
+-- 
+-- @
+--  ~False      -> True
+--  ~True       -> False
+--  True & P    -> P
+--  False & P   -> False
+--  True | P    -> True
+--  False | P   -> P
+--  True => P   -> P
+--  False => P  -> True
+--  P => True   -> P
+--  P => False  -> True
+--  True <=> P  -> P
+--  False <=> P -> ~P
+-- @
+-- 
+psimplify1 :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) f) = foldPropositional simplifyNotCombine (fromBool . not) simplifyNotAtom f
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _)            -> r
+            (Just False, (:&:), _)            -> fromBool False
+            (_,          (:&:), Just True)    -> l
+            (_,          (:&:), Just False)   -> fromBool False
+            (Just True,  (:|:), _)            -> fromBool True
+            (Just False, (:|:), _)            -> r
+            (_,          (:|:), Just True)    -> fromBool True
+            (_,          (:|:), Just False)   -> l
+            (Just True,  (:=>:), _)           -> r
+            (Just False, (:=>:), _)           -> fromBool True
+            (_,          (:=>:), Just True)   -> fromBool True
+            (_,          (:=>:), Just False)  -> (.~.) l
+            (Just False, (:<=>:), Just False) -> fromBool True
+            (Just True,  (:<=>:), _)          -> r
+            (Just False, (:<=>:), _)          -> (.~.) r
+            (_,          (:<=>:), Just True)  -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _                                 -> fm
+      simplifyNotCombine ((:~:) f) = f
+      simplifyNotCombine _ = fm
+      simplifyNotAtom x = (.~.) (atomic x)
+
+clauseNormalForm' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalForm' = simp purecnf . negationNormalForm
+
+clauseNormalForm :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalForm formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalForm' formula
+
+-- |I'm not sure of the clauseNormalForm functions above are wrong or just different.
+clauseNormalFormAlt' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalFormAlt' = simp purecnf' . negationNormalForm
+
+clauseNormalFormAlt :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalFormAlt formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalFormAlt' formula
+
+disjunctiveNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+disjunctiveNormalForm formula =
+    case clean (lists dnf) of
+      [] -> fromBool False
+      xss -> foldr1 (.|.) . map (foldr1 (.&.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      dnf = disjunctiveNormalForm' formula
+
+disjunctiveNormalForm' :: (PropositionalFormula formula atom, Eq formula) => formula -> Set.Set (Set.Set formula)
+disjunctiveNormalForm' = simp purednf . negationNormalForm
+
+simp :: forall formula atom. (PropositionalFormula formula atom) =>
+        (formula -> Set.Set (Set.Set formula)) -> formula -> Set.Set (Set.Set formula)
+simp purenf fm =
+    case (compare fm (fromBool False), compare fm (fromBool True)) of
+      (EQ, _) -> Set.empty
+      (_, EQ) -> Set.singleton Set.empty
+      _ ->cjs'
+    where
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (Set.isProperSubsetOf x) cjs))
+      cjs = Set.filter (not . trivial) (purenf (nnf fm)) :: Set.Set (Set.Set formula)
+
+-- |Harrison page 59.  Look for complementary pairs in a clause.
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection (Set.map (.~.) n) p
+    where (n, p) = Set.partition negated lits
+
+purecnf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+purednf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purednf fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.distrib (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+purecnf' :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf' fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.union (purecnf' p) (purecnf' q)
+      c (BinOp p (:|:) q) = Set.distrib (purecnf' p) (purecnf' q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+-- | Use this to implement foldAtoms
+foldAtomsPropositional :: PropositionalFormula pf atom => (r -> atom -> r) -> r -> pf -> r
+foldAtomsPropositional f i pf =
+        foldPropositional co (const i) (f i) pf
+        where
+          co ((:~:) pf') = foldAtomsPropositional f i pf'
+          co (BinOp p _ q) = foldAtomsPropositional f (foldAtomsPropositional f i q) p
+
+-- | Deprecated - use foldAtoms.
+overatoms :: forall formula atom r. PropositionalFormula formula atom => (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsPropositional (flip f) b fm
+
+mapAtomsPropositional :: forall formula atom. PropositionalFormula formula atom => (atom -> formula) -> formula -> formula
+mapAtomsPropositional f fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = mapAtomsPropositional f p
+      co (BinOp p op q) = binop (mapAtomsPropositional f p) op (mapAtomsPropositional f q)
+      tf flag = fromBool flag
+      at x = f x
+
+$(deriveSafeCopy 1 'base ''BinOp)
+$(deriveSafeCopy 1 'base ''Combination)
diff --git a/testdata/logic/Data/Logic/Classes/Skolem.hs b/testdata/logic/Data/Logic/Classes/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Skolem.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.Skolem where
+
+import Data.Logic.Classes.Variable (Variable)
+
+-- |This class shows how to convert between atomic Skolem functions
+-- and Ints.  We include a variable type as a parameter because we
+-- create skolem functions to replace an existentially quantified
+-- variable, and it can be helpful to retain a reference to the
+-- variable.
+class Variable v => Skolem f v | f -> v where
+    toSkolem :: v -> f
+    -- ^ Built a Skolem function from the given variable and number.
+    -- The number is generally obtained from the skolem monad.
+    isSkolem  :: f -> Bool
diff --git a/testdata/logic/Data/Logic/Classes/Term.hs b/testdata/logic/Data/Logic/Classes/Term.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Term.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Classes.Term
+    ( Term(..)
+    , Function
+    , convertTerm
+    , showTerm
+    , prettyTerm
+    , fvt
+    , tsubst
+    , funcs
+    ) where
+
+import Data.Generics (Data)
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Skolem
+import Data.Logic.Classes.Variable
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), brackets, hcat, text)
+
+class (Eq f, Ord f, Skolem f v, Data f, Pretty f) => Function f v
+
+class ( Ord term  -- For implementing Ord in Literal
+      , Variable v
+      , Function f v ) => Term term v f | term -> v f where
+    vt :: v -> term
+    -- ^ Build a term which is a variable reference.
+    fApp :: f -> [term] -> term
+    -- ^ Build a term by applying terms to an atomic function.  @f@
+    -- (atomic function) is one of the type parameters, this package
+    -- is mostly indifferent to its internal structure.
+    foldTerm :: (v -> r) -> (f -> [term] -> r) -> term -> r
+    -- ^ A fold for the term data type, which understands terms built
+    -- from a variable and a term built from the application of a
+    -- primitive function to other terms.
+    zipTerms :: (v -> v -> Maybe r) -> (f -> [term] -> f -> [term] -> Maybe r) -> term -> term -> Maybe r
+
+convertTerm :: forall term1 v1 f1 term2 v2 f2.
+               (Term term1 v1 f1,
+                Term term2 v2 f2) =>
+               (v1 -> v2) -> (f1 -> f2) -> term1 -> term2
+convertTerm convertV convertF term =
+    foldTerm v fn term
+    where
+      convertTerm' = convertTerm convertV convertF
+      v = vt . convertV
+      fn x ts = fApp (convertF x) (map convertTerm' ts)
+
+showTerm :: forall term v f. (Term term v f, Show v, Show f) =>
+            term -> String
+showTerm term =
+    foldTerm v f term
+    where
+      v :: v -> String
+      v v' = "vt (" ++ show v' ++ ")"
+      f :: f -> [term] -> String
+      f fn ts = "fApp (" ++ show fn ++ ") [" ++ intercalate "," (map showTerm ts) ++ "]"
+
+prettyTerm :: forall v f term. (Term term v f) =>
+              (v -> Doc)
+           -> (f -> Doc)
+           -> term
+           -> Doc
+prettyTerm pv pf t = foldTerm pv (\ fn ts -> pf fn <> brackets (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts)))) t
+
+fvt :: (Term term v f, Ord v) => term -> Set.Set v
+fvt tm = foldTerm Set.singleton (\ _ args -> Set.unions (map fvt args)) tm
+
+-- ------------------------------------------------------------------------- 
+-- Substitution within terms.                                                
+-- ------------------------------------------------------------------------- 
+
+tsubst :: (Term term v f, Ord v) => Map.Map v term -> term -> term
+tsubst sfn tm = foldTerm (\ x -> fromMaybe tm (Map.lookup x sfn)) (\ fn args -> fApp fn (map (tsubst sfn) args)) tm
+
+funcs :: (Term term v f, Ord f) => term -> Set.Set (f, Int)
+funcs tm =
+    foldTerm (const Set.empty)
+             (\ f args -> foldr (\ arg r -> Set.union (funcs arg) r) (Set.singleton (f, length args)) args)
+             tm
diff --git a/testdata/logic/Data/Logic/Classes/Variable.hs b/testdata/logic/Data/Logic/Classes/Variable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Classes/Variable.hs
@@ -0,0 +1,32 @@
+module Data.Logic.Classes.Variable
+    ( Variable(..)
+    , variants
+    , showVariable
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Pretty (Pretty)
+import qualified Data.Set as Set
+import Data.String (IsString)
+import Text.PrettyPrint (Doc)
+
+class (Ord v, IsString v, Data v, Pretty v) => Variable v where
+    variant :: v -> Set.Set v -> v
+    -- ^ Return a variable based on v but different from any set
+    -- element.  The result may be v itself if v is not a member of
+    -- the set.
+    prefix :: String -> v -> v
+    -- ^ Modify a variable by adding a prefix.  This unfortunately
+    -- assumes that v is "string-like" but at least one algorithm in
+    -- Harrison currently requires this.
+    prettyVariable :: v -> Doc
+    -- ^ Pretty print a variable
+
+-- | Return an infinite list of variations on v
+variants :: Variable v => v -> [v]
+variants v0 =
+    iter' Set.empty v0
+    where iter' s v = let v' = variant v s in v' : iter' (Set.insert v s) v'
+
+showVariable :: Variable v => v -> String
+showVariable v = "fromString (" ++ show (show (prettyVariable v)) ++ ")"
diff --git a/testdata/logic/Data/Logic/Failing.hs b/testdata/logic/Data/Logic/Failing.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Failing.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE  DeriveDataTypeable, StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Failing
+    ( Failing(Success, Failure)
+    , failing
+    ) where
+
+import Control.Applicative.Error
+import Data.Generics
+
+failing :: ([String] -> b) -> (a -> b) -> Failing a -> b
+failing f _ (Failure errs) = f errs
+failing _ f (Success a)    = f a
+
+instance Monad Failing where
+  return = Success
+  m >>= f =
+      case m of
+        (Failure errs) -> (Failure errs)
+        (Success a) -> f a
+  fail errMsg = Failure [errMsg]
+
+deriving instance Typeable1 Failing
+deriving instance Data a => Data (Failing a)
+deriving instance Read a => Read (Failing a)
+deriving instance Eq a => Eq (Failing a)
+deriving instance Ord a => Ord (Failing a)
diff --git a/testdata/logic/Data/Logic/Harrison/DP.hs b/testdata/logic/Data/Logic/Harrison/DP.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/DP.hs
@@ -0,0 +1,276 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DP
+    ( tests
+    , dpll
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate (Negatable, (.~.), negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.DefCNF (NumAtom(..), defcnfs)
+import Data.Logic.Harrison.Lib (allpairs, maximize', minimize', defined, setmapfilter, (|->))
+import Data.Logic.Harrison.Prop (negative, positive, trivial, tautology, cnf)
+import Data.Logic.Harrison.PropExamples (Atom(..), N, prime)
+import Data.Logic.Tests.HUnit
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Map as Map
+import qualified Data.Set.Extra as Set
+
+import Debug.Trace
+
+instance NumAtom (Atom N) where
+    ma n = P "p" n Nothing
+    ai (P _ n _) = n
+
+tests = convert (TestList [test01, test02, test03])
+
+-- ========================================================================= 
+-- The Davis-Putnam and Davis-Putnam-Loveland-Logemann procedures.           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- The DP procedure.                                                         
+-- ------------------------------------------------------------------------- 
+
+one_literal_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+one_literal_rule clauses =
+    case Set.minView (Set.filter (\ cl -> Set.size cl == 1) clauses) of
+      Nothing -> Failure ["one_literal_rule"]
+      Just (s, _) ->
+          let u = Set.findMin s in
+          let u' = (.~.) u in
+          let clauses1 = Set.filter (\ cl -> not (Set.member u cl)) clauses in
+          Success (Set.map (\ cl -> Set.delete u' cl) clauses1)
+
+affirmative_negative_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+affirmative_negative_rule clauses =
+  let (neg',pos) = Set.partition negative (Set.flatten clauses) in
+  let neg = Set.map (.~.) neg' in
+  let pos_only = Set.difference pos neg
+      neg_only = Set.difference neg pos in
+  let pure = Set.union pos_only (Set.map (.~.) neg_only) in
+  if Set.null pure
+  then Failure ["affirmative_negative_rule"]
+  else Success (Set.filter (\ cl -> Set.null (Set.intersection cl pure)) clauses)
+
+resolve_on :: forall lit atom. (Literal lit atom, Ord lit) =>
+              lit -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit)
+resolve_on p clauses =
+  let p' = (.~.) p
+      (pos,notpos) = Set.partition (Set.member p) clauses in
+  let (neg,other) = Set.partition (Set.member p') notpos in
+  let pos' = Set.map (Set.filter (\ l -> l /= p)) pos
+      neg' = Set.map (Set.filter (\ l -> l /= p')) neg in
+  let res0 = allpairs Set.union pos' neg' in
+  Set.union other (Set.filter (not . trivial) res0)
+
+resolution_blowup :: forall formula. (Negatable formula, Ord formula) =>
+                     Set.Set (Set.Set formula) -> formula -> Int
+resolution_blowup cls l =
+  let m = Set.size (Set.filter (Set.member l) cls)
+      n = Set.size (Set.filter (Set.member ((.~.) l)) cls) in
+  m * n - m - n
+
+resolution_rule :: forall lit atom. (Literal lit atom, Ord lit) =>
+                   Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+resolution_rule clauses =
+    let pvs = Set.filter positive (Set.flatten clauses) in
+    case minimize' (resolution_blowup clauses) pvs of
+      Just p -> Success (resolve_on p clauses)
+      Nothing -> Failure ["resolution_rule"]
+
+-- ------------------------------------------------------------------------- 
+-- Overall procedure.                                                        
+-- ------------------------------------------------------------------------- 
+
+dp :: forall lit atom. (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing Bool        
+dp clauses =
+  if Set.null clauses
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dp of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dp of
+                    Success x -> Success x
+                    Failure _ -> resolution_rule clauses >>= dp
+
+-- ------------------------------------------------------------------------- 
+-- Davis-Putnam satisfiability tester and tautology checker.                 
+-- ------------------------------------------------------------------------- 
+
+dpsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dpsat fm = dp (defcnfs fm :: Set.Set (Set.Set pf))
+
+dptaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dptaut fm = dpsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test01 = TestCase (assertEqual "dptaut(prime 11)" (Success True) (dptaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- The same thing but with the DPLL procedure.                               
+-- ------------------------------------------------------------------------- 
+
+posneg_count :: forall formula. (Negatable formula, Ord formula) =>
+                Set.Set (Set.Set formula) -> formula -> Int
+posneg_count cls l =                         
+  let m = Set.size(Set.filter (Set.member l) cls)                 
+      n = Set.size(Set.filter (Set.member ((.~.) l)) cls) in
+  m + n                                  
+
+dpll :: forall lit atom. (Literal lit atom, Ord lit) =>
+        Set.Set (Set.Set lit) -> Failing Bool
+dpll clauses =       
+  if clauses == Set.empty
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dpll of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dpll of
+                    Success x -> Success x
+                    Failure _ ->
+                        let pvs = Set.filter positive (Set.flatten clauses) in
+                        case maximize' (posneg_count clauses) pvs of
+                          Nothing -> Failure ["dpll"]
+                          Just p -> 
+                              case (dpll (Set.insert (Set.singleton p) clauses), dpll (Set.insert (Set.singleton ((.~.) p)) clauses)) of
+                                (Success a, Success b) -> Success (a || b)
+                                (Failure a, Failure b) -> Failure (a ++ b)
+                                (Failure a, _) -> Failure a
+                                (_, Failure b) -> Failure b
+
+dpllsat :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+           pf -> Failing Bool
+dpllsat fm = dpll(defcnfs fm :: Set.Set (Set.Set pf))
+
+dplltaut :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+            pf -> Failing Bool
+dplltaut fm = dpllsat ((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+test02 = TestCase (assertEqual "dplltaut(prime 11)" (Success True) (dplltaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- Iterative implementation with explicit trail instead of recursion.        
+-- ------------------------------------------------------------------------- 
+
+data TrailMix = Guessed | Deduced deriving (Eq, Ord)
+
+unassigned :: forall formula. (Negatable formula, Ord formula) =>
+              Set.Set (Set.Set formula) -> Set.Set (formula, TrailMix) -> Set.Set formula
+unassigned cls trail =
+    Set.difference (Set.flatten (Set.map (Set.map litabs) cls)) (Set.map (litabs . fst) trail)
+    where litabs p = if negated p then (.~.) p else p
+
+unit_subpropagate :: forall formula. (Negatable formula, Ord formula) =>
+                     (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+                  -> (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+unit_subpropagate (cls,fn,trail) =
+  let cls' = Set.map (Set.filter (not . defined fn . (.~.))) cls in
+  let uu cs =
+          case Set.minView cs of
+            Nothing -> Failure ["unit_subpropagate"]
+            Just (c, _) -> if Set.size cs == 1 && not (defined fn c)
+                           then Success cs
+                           else Failure ["unit_subpropagate"] in
+  let newunits = Set.flatten (setmapfilter uu cls') in
+  if Set.null newunits then (cls',fn,trail) else
+  let trail' = Set.fold (\ p t -> Set.insert (p,Deduced) t) trail newunits
+      fn' = Set.fold (\ u -> (u |-> ())) fn newunits in
+  unit_subpropagate (cls',fn',trail')
+
+unit_propagate :: forall t. (Negatable t, Ord t) =>
+                  (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+               -> (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+unit_propagate (cls,trail) =
+  let fn = Set.fold (\ (x,_) -> (x |-> ())) Map.empty trail in
+  let (cls',fn',trail') = unit_subpropagate (cls,fn,trail) in (cls',trail')
+
+backtrack :: forall t. Set.Set (t, TrailMix) -> Set.Set (t, TrailMix)
+backtrack trail =
+  case Set.minView trail of
+    Just ((p,Deduced), tt) -> backtrack tt
+    _ -> trail
+
+dpli :: forall atomic pf. (PropositionalFormula pf atomic, Ord pf) =>
+        Set.Set (Set.Set pf) -> Set.Set (pf, TrailMix) -> Failing Bool
+dpli cls trail =
+  let (cls', trail') = unit_propagate (cls, trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView trail of
+      Just ((p,Guessed), tt) -> dpli cls (Set.insert ((.~.) p, Deduced) tt)
+      _ -> Success False
+  else
+      case unassigned cls (trail' :: Set.Set (pf, TrailMix)) of
+        s | Set.null s -> Success True
+        ps -> case maximize' (posneg_count cls') ps of
+                Just p -> dpli cls (Set.insert (p :: pf, Guessed) trail')
+                Nothing -> Failure ["dpli"]
+
+dplisat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplisat fm = dpli (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplitaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplitaut fm = dplisat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- With simple non-chronological backjumping and learning.                   
+-- ------------------------------------------------------------------------- 
+
+backjump :: forall a. (Negatable a, Ord a) =>
+            Set.Set (Set.Set a) -> a -> Set.Set (a, TrailMix) -> Set.Set (a, TrailMix)
+backjump cls p trail =
+  case Set.minView (backtrack trail) of
+    Just ((q,Guessed), tt) ->
+        let (cls',trail') = unit_propagate (cls, Set.insert (p,Guessed) tt) in
+        if Set.member Set.empty cls' then backjump cls p tt else trail
+    _ -> trail
+
+dplb :: forall a. (Negatable a, Ord a) =>
+        Set.Set (Set.Set a) -> Set.Set (a, TrailMix) -> Failing Bool
+dplb cls trail =
+  let (cls',trail') = unit_propagate (cls,trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView (backtrack trail) of
+      Just ((p,Guessed), tt) ->
+        let trail'' = backjump cls p tt in
+        let declits = Set.filter (\ (_,d) -> d == Guessed) trail'' in
+        let conflict = Set.insert ((.~.) p) (Set.map ((.~.) . fst) declits) in
+        dplb (Set.insert conflict cls) (Set.insert ((.~.) p,Deduced) trail'')
+      _ -> Success False
+  else
+    case unassigned cls trail' of
+      s | Set.null s -> Success True
+      ps -> case maximize' (posneg_count cls') ps of
+              Just p -> dplb cls (Set.insert (p,Guessed) trail')
+              Nothing -> Failure ["dpib"]
+            
+dplbsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplbsat fm = dplb (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplbtaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplbtaut fm = dplbsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test03 = TestList [TestCase (assertEqual "dplitaut(prime 101)" (Success True) (dplitaut(prime 101 :: Formula (Atom N)))),
+                   TestCase (assertEqual "dplbtaut(prime 101)" (Success True) (dplbtaut(prime 101 :: Formula (Atom N))))]
diff --git a/testdata/logic/Data/Logic/Harrison/DefCNF.hs b/testdata/logic/Data/Logic/Harrison/DefCNF.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/DefCNF.hs
@@ -0,0 +1,160 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DefCNF
+    {- ( Atom
+    , NumAtom(ma, ai)
+    , defcnfs
+    , defcnf1
+    , defcnf2
+    , defcnf3
+    ) -} where
+
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), (.&.), (.|.), (.<=>.))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Propositional (PropositionalFormula(foldPropositional), overatoms)
+import Data.Logic.Harrison.Prop (nenf, simpcnf, cnf)
+import Data.Logic.Harrison.PropExamples (N)
+import qualified Data.Map as Map
+import qualified Data.Set.Extra as Set
+
+-- ========================================================================= 
+-- Definitional CNF.                                                         
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+{-
+START_INTERACTIVE;;
+cnf <<p <=> (q <=> r)>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Make a stylized variable and update the index.                            
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P a
+
+class NumAtom atom where
+    ma :: N -> atom
+    ai :: atom -> N
+
+instance NumAtom (Atom N) where
+    ma = P
+    ai (P n) = n
+
+mkprop :: forall pf atom. (PropositionalFormula pf atom, NumAtom atom) => N -> (pf, N)
+mkprop n = (atomic (ma n :: atom), n + 1)
+
+-- ------------------------------------------------------------------------- 
+-- Core definitional CNF procedure.                                          
+-- ------------------------------------------------------------------------- 
+
+maincnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+maincnf trip@(fm, _defs, _n) =
+    foldPropositional co tf at fm
+    where
+      co (BinOp p (:&:) q) = defstep (.&.) (p,q) trip
+      co (BinOp p (:|:) q) = defstep (.|.) (p,q) trip
+      co (BinOp p (:<=>:) q) = defstep (.<=>.) (p,q) trip
+      co (BinOp _ (:=>:) _) = trip
+      co ((:~:) _) = trip
+      tf _ = trip
+      at _ = trip
+
+defstep :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf -> pf -> pf) -> (pf, pf) -> (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+defstep op (p,q) (_fm, defs, n) =
+  let (fm1,defs1,n1) = maincnf (p,defs,n) in
+  let (fm2,defs2,n2) = maincnf (q,defs1,n1) in
+  let fm' = op fm1 fm2 in
+  case Map.lookup fm' defs2 of
+    Just _ -> (fm', defs2, n2)
+    Nothing -> let (v,n3) = mkprop n2 in (v, Map.insert v (v .<=>. fm') defs2,n3)
+
+-- ------------------------------------------------------------------------- 
+-- Make n large enough that "v_m" won't clash with s for any m >= n          
+-- ------------------------------------------------------------------------- 
+
+max_varindex :: NumAtom atom =>  atom -> Int -> Int
+max_varindex atom n = max n (ai atom)
+
+-- ------------------------------------------------------------------------- 
+-- Overall definitional CNF.                                                 
+-- ------------------------------------------------------------------------- 
+
+mk_defcnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) =>
+             ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)) -> pf -> Set.Set (Set.Set lit)
+mk_defcnf fn fm =
+  let fm' = nenf fm in
+  let n = 1 + overatoms max_varindex fm' 0 in
+  let (fm'',defs,_) = fn (fm',Map.empty,n) in
+  let (deflist {- :: [pf]-}) = Map.elems defs in
+  Set.unions (simpcnf fm'' : map simpcnf deflist)
+
+defcnf1 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf1 fm = cnf (mk_defcnf maincnf fm :: Set.Set (Set.Set lit))
+
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf1 <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version tweaked to exploit initial structure.                             
+-- ------------------------------------------------------------------------- 
+
+subcnf :: (PropositionalFormula pf atom, NumAtom atom) =>
+          ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int))
+       -> (pf -> pf -> pf)
+       -> pf
+       -> pf
+       -> (pf, Map.Map pf pf, Int)
+       -> (pf, Map.Map pf pf, Int)
+subcnf sfn op p q (_fm,defs,n) =
+  let (fm1,defs1,n1) = sfn (p,defs,n) in
+  let (fm2,defs2,n2) = sfn (q,defs1,n1) in
+  (op fm1 fm2, defs2, n2)
+
+orcnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+orcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:|:) q) = subcnf orcnf (.|.) p q trip
+      co _ = maincnf trip
+
+andcnf :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> orcnf trip) (\ _ -> orcnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf (.&.) p q trip
+      co _ = orcnf trip
+
+defcnfs :: (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+defcnfs fm = mk_defcnf andcnf fm
+
+defcnf2 :: forall pf lit atom.(PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf2 fm = cnf (defcnfs fm :: Set.Set (Set.Set lit))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version that guarantees 3-CNF.                                            
+-- ------------------------------------------------------------------------- 
+
+andcnf3 :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf3 trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf3 (.&.) p q trip
+      co _ = maincnf trip
+
+defcnf3 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf3 fm = cnf (mk_defcnf andcnf3 fm :: Set.Set (Set.Set lit))
diff --git a/testdata/logic/Data/Logic/Harrison/Equal.hs b/testdata/logic/Data/Logic/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Equal.hs
@@ -0,0 +1,331 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, RankNTypes, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Equal
+{-  ( function_congruence
+    , equalitize
+    ) -} where
+
+-- ========================================================================= 
+-- First order logic with equality.                                          
+--                                                                           
+-- Copyright (co) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine ((∧), (⇒))
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Equals (AtomEq(..), applyEq, (.=.), PredicateName(..), funcsAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), (∀))
+import Data.Logic.Classes.Formula (Formula(atomic, foldAtoms))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (atom_union)
+import Data.Logic.Harrison.Lib ((∅))
+-- import Data.Logic.Harrison.Skolem (functions)
+import qualified Data.Set as Set
+import Data.String (IsString(fromString))
+
+-- is_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Bool
+-- is_eq = foldFirstOrder (\ _ _ _ -> False) (\ _ -> False) (\ _ -> False) (foldAtomEq (\ _ _ -> False) (\ _ -> False) (\ _ _ -> True))
+-- 
+-- mk_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+-- mk_eq = (.=.)
+-- 
+-- dest_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing (term, term)
+-- dest_eq fm =
+--     foldFirstOrder (\ _ _ _ -> err) (\ _ -> err) (\ _ -> err) at fm
+--     where
+--       at = foldAtomEq (\ _ _ -> err) (\ _ -> err) (\ s t -> Success (s, t))
+--       err = Failure ["dest_eq: not an equation"]
+-- 
+-- lhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- lhs eq = dest_eq eq >>= return . fst
+-- rhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- rhs eq = dest_eq eq >>= return . snd
+
+-- ------------------------------------------------------------------------- 
+-- The set of predicates in a formula.                                       
+-- ------------------------------------------------------------------------- 
+
+predicates :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) => formula -> Set.Set (PredicateName p)
+predicates fm =
+    atom_union pair fm
+    where -- pair :: atom -> Set.Set (p, Int)
+          pair = foldAtomEq (\ p a -> Set.singleton (Named p (maybe (length a)
+                                                                    (\ n -> if n /= length a then n else error "arity mismatch")
+                                                                    (arity p))))
+                            (\ x -> Set.singleton (Named (fromBool x) 0))
+                            (\ _ _ -> Set.singleton Equals)
+
+{-
+-- | Traverse a formula and pass all (predicates, arity) pairs to a function.
+-- To collect
+foldPredicates :: forall formula atom term v p r. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) =>
+                  (PredicateName p -> Maybe Int -> r -> r) -> formula -> r -> r
+foldPredicates f fm acc =
+    foldFirstOrder qu co tf at fm
+    where
+      fold = foldPredicates f
+      qu _ _ p = fold p acc
+      co (BinOp l _ r) = fold r (fold l acc)
+      co ((:~:) p) = fold p acc
+      tf x = fold (fromBool x) acc
+      at = foldAtomEq ap tf eq
+      ap p _ = f (Name p) (arity p) acc
+      eq _ _ = f Equals (Just 2) acc
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Code to generate equality axioms for functions.                           
+-- ------------------------------------------------------------------------- 
+
+function_congruence :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f) =>
+                       (f, Int) -> Set.Set fof
+function_congruence (_,0) = (∅)
+function_congruence (f,n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x :: [v]
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y :: [v]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = fApp f args_x .=. fApp f args_y
+  
+-- ------------------------------------------------------------------------- 
+-- And for predicates.                                                       
+-- ------------------------------------------------------------------------- 
+
+predicate_congruence :: (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord p) =>
+                        PredicateName p -> Set.Set fof
+predicate_congruence Equals = Set.empty
+predicate_congruence (Named _ 0) = Set.empty
+predicate_congruence (Named p n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = atomic (applyEq p args_x) ⇒ atomic (applyEq p args_y)
+
+-- ------------------------------------------------------------------------- 
+-- Hence implement logic with equality just by adding equality "axioms".     
+-- ------------------------------------------------------------------------- 
+
+equivalence_axioms :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord fof) => Set.Set fof
+equivalence_axioms =
+    Set.fromList
+    [(∀) "x" (x .=. x),
+     (∀) "x" ((∀) "y" ((∀) "z" (x .=. y ∧ x .=. z ⇒ y .=. z)))]
+    where
+      x :: term
+      x = vt (fromString "x")
+      y :: term
+      y = vt (fromString "y")
+      z :: term
+      z = vt (fromString "z")
+
+equalitize :: forall formula atom term v p f. (FirstOrderFormula formula atom v, Formula formula atom, AtomEq atom p term, Ord p, Show p, Term term v f, Ord formula, Ord f) =>
+              formula -> formula
+equalitize fm =
+    if not (Set.member Equals allpreds)
+    then fm
+    else foldr1 (∧) (Set.toList axioms) ⇒ fm
+    where
+      axioms = Set.fold (Set.union . function_congruence) (Set.fold (Set.union . predicate_congruence) equivalence_axioms preds) (functions' funcsAtomEq' fm)
+      funcsAtomEq' :: atom -> Set.Set (f, Int)
+      funcsAtomEq' = funcsAtomEq
+      allpreds = predicates fm
+      preds = Set.delete Equals allpreds
+
+functions' :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions' fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- Other variants not mentioned in book.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+{- ************
+
+(meson ** equalitize)
+ <<(forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x * 1 = x) /\
+   (forall x. x * x = 1)
+   ==> forall x y. x * y  = y * x>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With symmetry at leaves and one-sided congruences (Size = 16, 54659 s).   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x. x = x) /\
+   (forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x y z. =((x * y) * z,x * (y * z))) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x = 1 * x) /\
+   (forall x. i(x) * x = 1) /\
+   (forall x. 1 = i(x) * x) /\
+   (forall x y. x = y ==> i(x) = i(y)) /\
+   (forall x y z. x = y ==> x * z = y * z) /\
+   (forall x y z. x = y ==> z * x = z * y) /\
+   (forall x y z. x = y /\ y = z ==> x = z)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Newer version of stratified equalities.                                   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cchain(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cchain(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall x x' u. x = x' /\ achain(i(x'),u) ==> cchain(i(x),u)) /\
+   (forall x x' y y' u.
+        x = x' /\ y = y' /\ achain(x' * y',u) ==> cchain(x * y,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cong(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cong(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Showing congruence closure.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm = equalitize
+ <<forall c. f(f(f(f(f(c))))) = c /\ f(f(f(c))) = c ==> f(c) = c>>;;
+
+time meson fm;;
+
+let fm =
+ <<axiom(f(f(f(f(f(c))))),c) /\
+   axiom(c,f(f(f(f(f(c)))))) /\
+   axiom(f(f(f(c))),c) /\
+   axiom(c,f(f(f(c)))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t) /\
+   (forall x y. x = y ==> cong(f(x),f(y)))
+   ==> f(c) = c>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- With stratified equalities.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x. eqA (x,x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqT (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y z. eqA (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With transitivity chains...                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y. eqA (w,x) ==> eqC (w * y,x * y)) /\
+   (forall w x y. eqC (w,x) ==> eqC (w * y,x * y)) /\
+   (forall x y z. eqA (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqC (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1) \/ eqC (x * i(x),1)>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Enforce canonicity (proof size = 20).                                     
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eq1(x * (y * z),(x * y) * z)) /\
+   (forall x y z. eq1((x * y) * z,x * (y * z))) /\
+   (forall x. eq1(1 * x,x)) /\
+   (forall x. eq1(x,1 * x)) /\
+   (forall x. eq1(i(x) * x,1)) /\
+   (forall x. eq1(1,i(x) * x)) /\
+   (forall x y z. eq1(x,y) ==> eq1(x * z,y * z)) /\
+   (forall x y z. eq1(x,y) ==> eq1(z * x,z * y)) /\
+   (forall x y z. eq1(x,y) /\ eq2(y,z) ==> eq2(x,z)) /\
+   (forall x y. eq1(x,y) ==> eq2(x,y))
+   ==> forall x. eq2(x,i(x))>>;;
+
+time meson fm;;
+
+***************** -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/FOL.hs b/testdata/logic/Data/Logic/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/FOL.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.FOL
+    ( eval
+    , list_disj
+    , list_conj
+    , var
+    , fv
+    -- , fv'
+    , subst
+    -- , subst'
+    , generalize
+    ) where
+
+import Data.Logic.Classes.Apply (Apply(..), apply)
+import Data.Logic.Classes.Atom (Atom(allVariables, substitute))
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants (fromBool), true, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term(vt), fvt)
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (on_atoms)
+import Data.Logic.Harrison.Lib ((|->), setAny)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Prelude hiding (pred)
+
+-- =========================================================================
+-- Basic stuff for first order logic.                                       
+--                                                                          
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.) 
+-- =========================================================================
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: FirstOrderFormula formula atom v => formula -> (atom -> Bool) -> Bool
+eval fm v =
+    foldFirstOrder qu co id at fm
+    where
+      qu _ _ p = eval p v
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at = v
+
+list_conj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (FirstOrderFormula formula atom v, Ord formula) =>
+          Set.Set formula -> (atom -> Bool) -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+-- -------------------------------------------------------------------------
+-- Special case of applying a subfunction to the top *terms*.               
+-- -------------------------------------------------------------------------
+
+on_formula :: forall fol atom term v p. (FirstOrderFormula fol atom v, Apply atom p term) => (term -> term) -> fol -> fol
+on_formula f = on_atoms (foldApply (\ p ts -> atomic (apply p (map f ts) :: atom)) fromBool)
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of terms.                                                         
+-- ------------------------------------------------------------------------- 
+
+{-
+let is_const_name s = forall numeric (explode s) or s = "nil";;
+
+let rec parse_atomic_term vs inp =
+  match inp with
+    [] -> failwith "term expected"
+  | "("::rest -> parse_bracketed (parse_term vs) ")" rest
+  | "-"::rest -> papply (fun t -> Fn("-",[t])) (parse_atomic_term vs rest)
+  | f::"("::")"::rest -> Fn(f,[]),rest
+  | f::"("::rest ->
+      papply (fun args -> Fn(f,args))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | a::rest ->
+      (if is_const_name a & not(mem a vs) then Fn(a,[]) else Var a),rest
+
+and parse_term vs inp =
+  parse_right_infix "::" (fun (e1,e2) -> Fn("::",[e1;e2]))
+    (parse_right_infix "+" (fun (e1,e2) -> Fn("+",[e1;e2]))
+       (parse_left_infix "-" (fun (e1,e2) -> Fn("-",[e1;e2]))
+          (parse_right_infix "*" (fun (e1,e2) -> Fn("*",[e1;e2]))
+             (parse_left_infix "/" (fun (e1,e2) -> Fn("/",[e1;e2]))
+                (parse_left_infix "^" (fun (e1,e2) -> Fn("^",[e1;e2]))
+                   (parse_atomic_term vs)))))) inp;;
+
+let parset = make_parser (parse_term []);;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas.                                                      
+-- ------------------------------------------------------------------------- 
+
+let parse_infix_atom vs inp =       
+  let tm,rest = parse_term vs inp in
+  if exists (nextin rest) ["="; "<"; "<="; ">"; ">="] then                     
+        papply (fun tm' -> Atom(R(hd rest,[tm;tm'])))                          
+               (parse_term vs (tl rest))                                       
+  else failwith "";;
+                                                               
+let parse_atom vs inp =
+  try parse_infix_atom vs inp with Failure _ ->                                
+  match inp with                                                               
+  | p::"("::")"::rest -> Atom(R(p,[])),rest                                    
+  | p::"("::rest ->
+      papply (fun args -> Atom(R(p,args)))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | p::rest when p <> "(" -> Atom(R(p,[])),rest
+  | _ -> failwith "parse_atom";;
+                                                                               
+let parse = make_parser                                                        
+  (parse_formula (parse_infix_atom,parse_atom) []);;              
+
+-- ------------------------------------------------------------------------- 
+-- Set up parsing of quotations.                                             
+-- ------------------------------------------------------------------------- 
+
+let default_parser = parse;;
+
+let secondary_parser = parset;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printing of terms.                                                        
+-- ------------------------------------------------------------------------- 
+{-
+let rec print_term prec fm =
+  match fm with
+    Var x -> print_string x
+  | Fn("^",[tm1;tm2]) -> print_infix_term true prec 24 "^" tm1 tm2
+  | Fn("/",[tm1;tm2]) -> print_infix_term true prec 22 " /" tm1 tm2
+  | Fn("*",[tm1;tm2]) -> print_infix_term false prec 20 " *" tm1 tm2
+  | Fn("-",[tm1;tm2]) -> print_infix_term true prec 18 " -" tm1 tm2
+  | Fn("+",[tm1;tm2]) -> print_infix_term false prec 16 " +" tm1 tm2
+  | Fn("::",[tm1;tm2]) -> print_infix_term false prec 14 "::" tm1 tm2
+  | Fn(f,args) -> print_fargs f args
+
+and print_fargs f args =
+  print_string f;
+  if args = [] then () else
+   (print_string "(";
+    open_box 0;
+    print_term 0 (hd args); print_break 0 0;
+    do_list (fun t -> print_string ","; print_break 0 0; print_term 0 t)
+            (tl args);
+    close_box();
+    print_string ")")
+
+and print_infix_term isleft oldprec newprec sym p q =
+  if oldprec > newprec then (print_string "("; open_box 0) else ();
+  print_term (if isleft then newprec else newprec+1) p;
+  print_string sym;
+  print_break (if String.sub sym 0 1 = " " then 1 else 0) 0;
+  print_term (if isleft then newprec+1 else newprec) q;
+  if oldprec > newprec then (close_box(); print_string ")") else ();;
+
+let printert tm =
+  open_box 0; print_string "<<|";
+  open_box 0; print_term 0 tm; close_box();
+  print_string "|>>"; close_box();;
+
+#install_printer printert;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas.                                                     
+-- ------------------------------------------------------------------------- 
+
+let print_atom prec (R(p,args)) =
+  if mem p ["="; "<"; "<="; ">"; ">="] & length args = 2
+  then print_infix_term false 12 12 (" "^p) (el 0 args) (el 1 args)
+  else print_fargs p args;;
+
+let print_fol_formula = print_qformula print_atom;;
+
+#install_printer print_fol_formula;;
+
+-- ------------------------------------------------------------------------- 
+-- Examples in the main text.                                                
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+<<forall x y. exists z. x < z /\ y < z>>;;
+
+<<~(forall x. P(x)) <=> exists y. ~P(y)>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Free variables in terms and formulas.                                     
+-- ------------------------------------------------------------------------- 
+
+-- | Return all variables occurring in a formula.
+var :: forall formula atom term v.
+       (FirstOrderFormula formula atom v,
+        Atom atom term v) => formula -> Set.Set v
+var fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.insert x (var p)
+      co ((:~:) p) = var p
+      co (BinOp p _ q) = Set.union (var p) (var q)
+      tf _ = Set.empty
+      at :: atom -> Set.Set v
+      at = allVariables
+
+-- | Return the variables that occur free in a formula.
+fv :: forall formula atom term v.
+      (FirstOrderFormula formula atom v,
+       Atom atom term v) => formula -> Set.Set v
+fv fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.delete x (fv p)
+      co ((:~:) p) = fv p
+      co (BinOp p _ q) = Set.union (fv p) (fv q)
+      tf _ = Set.empty
+      at = allVariables
+
+-- | Return the variables in a propositional formula.
+fv' :: forall formula atom term v. (PropositionalFormula formula atom, Atom atom term v, Ord v) => formula -> Set.Set v
+fv' fm =
+    foldPropositional co tf allVariables fm
+    where
+      co ((:~:) p) = fv' p
+      co (BinOp p _ q) = Set.union (fv' p) (fv' q)
+      tf _ = Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Universal closure of a formula.                                           
+-- ------------------------------------------------------------------------- 
+
+generalize :: (FirstOrderFormula formula atom v, Atom atom term v) => formula -> formula
+generalize fm = Set.fold for_all fm (fv fm)
+
+-- ------------------------------------------------------------------------- 
+-- Substitution in formulas, with variable renaming.                         
+-- ------------------------------------------------------------------------- 
+
+subst :: (FirstOrderFormula formula atom v,
+          Term term v f,
+          Atom atom term v) =>
+         Map.Map v term -> formula -> formula
+subst env fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x p = quant op x' (subst ((x |-> vt x') env) p)
+          where
+            x' = if setAny (\ y -> Set.member x (fvt (fromMaybe (vt y) (Map.lookup y env)))) (Set.delete x (fv p))
+                 then variant x (fv (subst (Map.delete x env) p))
+                 else x
+      co ((:~:) p) = ((.~.) (subst env p))
+      co (BinOp p op q) = binop (subst env p) op (subst env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+subst' :: (PropositionalFormula formula atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          Map.Map v term -> formula -> formula
+subst' env fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = ((.~.) (subst' env p))
+      co (BinOp p op q) = binop (subst' env p) op (subst' env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+{-
+-- |Replace each free occurrence of variable old with term new.
+substitute :: forall formula atom term v f. (FirstOrderFormula formula atom v, Term term v f) => v -> term -> (atom -> formula) -> formula -> formula
+substitute old new atom formula =
+    foldTerm (\ new' -> if old == new' then formula else substitute' formula)
+             (\ _ _ -> substitute' formula)
+             new
+    where
+      substitute' =
+          foldFirstOrder -- If the old variable appears in a quantifier
+                -- we can stop doing the substitution.
+                (\ q v f' -> quant q v (if old == v then f' else substitute' f'))
+                (\ cm -> case cm of
+                           ((:~:) f') -> combine ((:~:) (substitute' f'))
+                           (BinOp f1 op f2) -> combine (BinOp (substitute' f1) op (substitute' f2)))
+                fromBool
+                atom
+-}
+{-
+    substitute old new atom formula
+    where 
+      atom = foldAtomEq (\ p ts -> pApp p (map st ts)) fromBool (\ t1 t2 -> st t1 .=. st t2)
+      st :: term -> term
+      st t = foldTerm sv (\ func ts -> fApp func (map st ts)) t
+      sv v = if v == old then new else vt v
+-}
+
diff --git a/testdata/logic/Data/Logic/Harrison/Formulas/FirstOrder.hs b/testdata/logic/Data/Logic/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,211 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.FirstOrder
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Negate ((.~.))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: FirstOrderFormula formula atom v => formula -> formula
+antecedent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = err
+      err = error "antecedent"
+
+consequent :: FirstOrderFormula formula atom v => formula -> formula
+consequent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = err
+      err = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+on_atoms f fm =
+    foldFirstOrder qu co tf at fm
+    where 
+      qu op v fm' = quant op v (on_atoms f fm')
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at = f
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: FirstOrderFormula formula atom v => (atom -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldFirstOrder qu co tf pr fm
+    where
+      qu _ _ p = over_atoms f p b
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      pr atom = f atom b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (FirstOrderFormula formula atom v, Ord b) => (atom -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/logic/Data/Logic/Harrison/Formulas/Propositional.hs b/testdata/logic/Data/Logic/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,208 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.Propositional
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+--import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: PropositionalFormula formula atomic => formula -> formula
+antecedent formula =
+    foldPropositional c (error "antecedent") (error "antecedent") formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = error "antecedent"
+
+consequent :: PropositionalFormula formula atomic => formula -> formula
+consequent formula =
+    foldPropositional c (error "consequent") (error "consequent") formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: PropositionalFormula formula atomic => (atomic -> formula) -> formula -> formula
+on_atoms f fm =
+    foldPropositional co tf at fm
+    where 
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at x = f x
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: (PropositionalFormula formula atomic) => (atomic -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      at x = f x b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (PropositionalFormula formula atomic, Ord b) => (atomic -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/logic/Data/Logic/Harrison/Herbrand.hs b/testdata/logic/Data/Logic/Harrison/Herbrand.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Herbrand.hs
@@ -0,0 +1,309 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Harrison.Herbrand where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom(substitute, freeVariables))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term, fApp)
+import Data.Logic.Harrison.DP (dpll)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (distrib', allpairs)
+import Data.Logic.Harrison.Normal (trivial)
+import Data.Logic.Harrison.Prop (eval, simpcnf, simpdnf)
+import Data.Logic.Harrison.Skolem (runSkolem, skolemize, functions)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Data.String (IsString(..))
+
+-- ========================================================================= 
+-- Relation between FOL and propositonal logic; Herbrand theorem.            
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Propositional valuation.                                                  
+-- ------------------------------------------------------------------------- 
+
+pholds :: (PropositionalFormula formula atom, Ord atom) => (Map.Map atom Bool) -> formula -> Bool
+pholds d fm = eval fm d
+
+-- ------------------------------------------------------------------------- 
+-- Get the constants for Herbrand base, adding nullary one if necessary.     
+-- ------------------------------------------------------------------------- 
+
+herbfuns :: forall pf atom term v f. (PropositionalFormula pf atom, Formula pf atom, Atom atom term v, Term term v f, IsString f, Ord f) =>
+            (atom -> Set.Set (f, Int))
+         -> pf
+         -> (Set.Set (f, Int), Set.Set (f, Int))
+herbfuns fa fm =
+  let (cns,fns) = Set.partition (\ (_,ar) -> ar == 0) (functions fa fm) in
+  if Set.null cns then (Set.singleton (fromString "c",0),fns) else (cns,fns)
+
+-- ------------------------------------------------------------------------- 
+-- Enumeration of ground terms and m-tuples, ordered by total fns.           
+-- ------------------------------------------------------------------------- 
+
+groundterms :: forall term v f. (Term term v f) =>
+               Set.Set term -> Set.Set (f, Int) -> Int -> Set.Set term
+groundterms cntms _ 0 = cntms
+groundterms cntms funcs n =
+    Set.fold terms Set.empty funcs
+    where
+      terms (f,m) l = Set.union (Set.map (fApp f) (groundtuples cntms funcs (n - 1) m)) l
+
+groundtuples :: forall term v f. (Term term v f) =>
+                Set.Set term -> Set.Set (f, Int) -> Int -> Int -> Set.Set [term]
+groundtuples _ _ 0 0 = Set.singleton []
+groundtuples _ _ _ 0 = Set.empty
+groundtuples cntms funcs n m =
+    Set.fold tuples Set.empty (Set.fromList [0 .. n])
+    where 
+      tuples k l = Set.union (allpairs (:) (groundterms cntms funcs k) (groundtuples cntms funcs (n - k) (m - 1))) l
+
+-- ------------------------------------------------------------------------- 
+-- Iterate modifier "mfn" over ground terms till "tfn" fails.                
+-- ------------------------------------------------------------------------- 
+
+herbloop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v) =>
+            (Set.Set (Set.Set lit) -> (lit -> lit) -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit))
+         -> (Set.Set (Set.Set lit) -> Failing Bool)
+         -> Set.Set (Set.Set lit)
+         -> Set.Set term
+         -> Set.Set (f, Int)
+         -> [v]
+         -> Int
+         -> Set.Set (Set.Set lit)
+         -> Set.Set [term]
+         -> Set.Set [term]
+         -> Failing (Set.Set [term])
+herbloop mfn tfn fl0 cntms funcs fvs n fl tried tuples =
+{-
+  print_string(string_of_int(length tried) ++ " ground instances tried; " ++
+               string_of_int(length fl) ++ " items in list")
+  print_newline();
+-}
+  case Set.minView tuples of
+    Nothing ->
+          let newtups = groundtuples cntms funcs n (length fvs) in
+          herbloop mfn tfn fl0 cntms funcs fvs (n + 1) fl tried newtups
+    Just (tup, tups) ->
+        let fpf' = Map.fromList (zip fvs tup) in
+        let fl' = mfn fl0 (subst' fpf') fl in
+        case tfn fl' of
+          Failure msgs -> Failure msgs
+          Success x ->
+              if not x
+              then Success (Set.insert tup tried)
+              else herbloop mfn tfn fl0 cntms funcs fvs n fl' (Set.insert tup tried) tups
+
+subst' :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => Map.Map v term -> lit -> lit
+subst' env fm =
+    mapAtoms (atomic . substitute') fm
+    where substitute' :: atom -> atom
+          substitute' = substitute env
+
+-- ------------------------------------------------------------------------- 
+-- Hence a simple Gilmore-type procedure.                                    
+-- ------------------------------------------------------------------------- 
+
+gilmore_loop :: (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit)
+             -> Set.Set term
+             -> Set.Set (f, Int)
+             -> [v]
+             -> Int
+             -> Set.Set (Set.Set lit)
+             -> Set.Set [term]
+             -> Set.Set [term]
+             -> Failing (Set.Set [term])
+gilmore_loop =
+    herbloop mfn (Success . not . Set.null)
+    where
+      mfn djs0 ifn djs = Set.filter (not . trivial) (distrib' (Set.map (Set.map ifn) djs0) djs)
+
+gilmore :: forall fof pf atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            Atom atom term v,
+            IsString f,
+            Ord pf) =>
+           (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+gilmore fa fm =
+  let sfm = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union s (freeVariables a)) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+  gilmore_loop (simpdnf sfm :: Set.Set (Set.Set pf)) cntms funcs (fvs) 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- First example and a little tracing.                                       
+-- ------------------------------------------------------------------------- 
+{-
+test01 =
+    let fm = exists "x" (for_all "y" (pApp "p" [vt "x"] .=>. pApp "p" [vt "y"]))
+        sfm = skolemize ((.~.) fm) in
+    TestList [TestCase (assertEqual "gilmore 1" 2 (gilmore fm))]
+
+START_INTERACTIVE;;
+gilmore <<exists x. forall y. P(x) ==> P(y)>>;;
+
+let sfm = skolemize(Not <<exists x. forall y. P(x) ==> P(y)>>);;
+
+-- ------------------------------------------------------------------------- 
+-- Quick example.                                                            
+-- ------------------------------------------------------------------------- 
+
+let p24 = gilmore
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x))
+   ==> (exists x. P(x) /\ R(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Slightly less easy example.                                               
+-- ------------------------------------------------------------------------- 
+
+let p45 = gilmore
+ <<(forall x. P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))
+              ==> (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==> L(y)) /\
+                      (forall y. G(y) /\ H(x,y) ==> J(x,y)))
+   ==> (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Apparently intractable example.                                           
+-- ------------------------------------------------------------------------- 
+
+{-
+
+let p20 = gilmore
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+-}
+
+
+-- ------------------------------------------------------------------------- 
+-- The Davis-Putnam procedure for first order logic.                         
+-- ------------------------------------------------------------------------- 
+
+dp_mfn :: (Ord b, Ord a) =>
+          Set.Set (Set.Set a)
+       -> (a -> b)
+       -> Set.Set (Set.Set b)
+       -> Set.Set (Set.Set b)
+dp_mfn cjs0 ifn cjs = Set.union (Set.map (Set.map ifn) cjs0) cjs
+
+dp_loop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+           Set.Set (Set.Set lit)
+        -> Set.Set term
+        -> Set.Set (f, Int)
+        -> [v]
+        -> Int
+        -> Set.Set (Set.Set lit)
+        -> Set.Set [term]
+        -> Set.Set [term]
+        -> Failing (Set.Set [term])
+dp_loop = herbloop dp_mfn dpll
+
+davisputnam :: forall fof atom term v lit f.
+               (FirstOrderFormula fof atom v,
+                PropositionalFormula lit atom,
+                Literal lit atom,
+                Term term v f,
+                Atom atom term v,
+                IsString f,
+                Ord lit) =>
+               (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam fa fm =
+  let (sfm :: lit) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c [] :: term) consts in
+  dp_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- Show how much better than the Gilmore procedure this can be.              
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+let p20 = davisputnam
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Try to cut out useless instantiations in final result.                    
+-- ------------------------------------------------------------------------- 
+
+dp_refine :: (Literal lit atom, Atom atom term v, Term term v f) =>
+             Set.Set (Set.Set lit) -> [v] -> Set.Set [term] -> Set.Set [term] -> Failing (Set.Set [term])
+dp_refine cjs0 fvs dknow need =
+    case Set.minView dknow of
+      Nothing -> Success need
+      Just (cl, dknow') ->
+          let mfn = dp_mfn cjs0 . subst' . Map.fromList . zip fvs in
+          dpll (Set.fold mfn Set.empty (Set.union need dknow')) >>= \ flag ->
+          if flag then return (Set.insert cl need) else return need >>=
+          dp_refine cjs0 fvs dknow'
+
+dp_refine_loop :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+                  Set.Set (Set.Set lit)
+               -> Set.Set term
+               -> Set.Set (f, Int)
+               -> [v]
+               -> Int
+               -> Set.Set (Set.Set lit)
+               -> Set.Set [term]
+               -> Set.Set [term]
+               -> Failing (Set.Set [term])
+dp_refine_loop cjs0 cntms funcs fvs n cjs tried tuples =
+    dp_loop cjs0 cntms funcs fvs n cjs tried tuples >>= \ tups ->
+    dp_refine cjs0 fvs tups Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Show how few of the instances we really need. Hence unification!          
+-- ------------------------------------------------------------------------- 
+
+davisputnam' :: forall fof atom term lit v f pf.
+                (FirstOrderFormula fof atom v,
+                 Literal lit atom,
+                 PropositionalFormula pf atom, -- Formula pf atom,
+                 Term term v f,
+                 Atom atom term v,
+                 IsString f) =>
+                (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam' fa fm =
+    let (sfm :: pf) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+    let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+        (consts,funcs) = herbfuns fa sfm in
+    let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+    dp_refine_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+{-
+START_INTERACTIVE;;
+let p36 = davisputnam'
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+                ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+   ==> (forall x. exists y. H(x,y))>>;;
+
+let p29 = davisputnam'
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/LICENSE.txt b/testdata/logic/Data/Logic/Harrison/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/LICENSE.txt
@@ -0,0 +1,35 @@
+IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+By downloading, copying, installing or using the software you agree
+to this license.  If you do not agree to this license, do not
+download, install, copy or use the software.
+
+Copyright (c) 2003-2007, John Harrison
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* The name of John Harrison may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/testdata/logic/Data/Logic/Harrison/Lib.hs b/testdata/logic/Data/Logic/Harrison/Lib.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Lib.hs
@@ -0,0 +1,846 @@
+{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}
+{-# OPTIONS_GHC -Wall -fno-warn-unused-binds #-}
+module Data.Logic.Harrison.Lib
+    ( tests
+    , setAny
+    , setAll
+    -- , itlist2
+    -- , itlist  -- same as foldr with last arguments flipped
+    , tryfind
+    , settryfind
+    -- , end_itlist -- same as foldr1
+    , (|=>)
+    , (|->)
+    , fpf
+    , defined
+    , apply
+    , exists
+    , tryApplyD
+    , allpairs
+    , distrib'
+    , image
+    , optimize
+    , minimize
+    , maximize
+    , optimize'
+    , minimize'
+    , maximize'
+    , can
+    , allsets
+    , allsubsets
+    , allnonemptysubsets
+    , mapfilter
+    , setmapfilter
+    , (∅)
+    ) where
+
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+import Data.Maybe
+import qualified Data.Set as Set
+import Test.HUnit (Test(TestCase, TestList, TestLabel), assertEqual)
+
+(∅) :: Set.Set a
+(∅) = Set.empty
+
+tests :: Test
+tests = TestLabel "Data.Logic.Harrison.Lib" $ TestList [test01]
+
+setAny :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAny f s = Set.member True (Set.map f s)
+
+setAll :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAll f s = not (Set.member False (Set.map f s))
+
+{-
+(* ========================================================================= *)
+(* Misc library functions to set up a nice environment.                      *)
+(* ========================================================================= *)
+
+let identity x = x;;
+
+let ( ** ) = fun f g x -> f(g x);;
+
+(* ------------------------------------------------------------------------- *)
+(* GCD and LCM on arbitrary-precision numbers.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let gcd_num n1 n2 =
+  abs_num(num_of_big_int
+      (Big_int.gcd_big_int (big_int_of_num n1) (big_int_of_num n2)));;
+
+let lcm_num n1 n2 = abs_num(n1 */ n2) // gcd_num n1 n2;;
+
+(* ------------------------------------------------------------------------- *)
+(* A useful idiom for "non contradictory" etc.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let non p x = not(p x);;
+
+(* ------------------------------------------------------------------------- *)
+(* Kind of assertion checking.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let check p x = if p(x) then x else failwith "check";;
+
+(* ------------------------------------------------------------------------- *)
+(* Repetition of a function.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec funpow n f x =
+  if n < 1 then x else funpow (n-1) f (f x);;
+-}
+-- let can f x = try f x; true with Failure _ -> false;;
+can :: (t -> Failing a) -> t -> Bool
+can f x = failing (const True) (const False) (f x)
+
+{-
+let rec repeat f x = try repeat f (f x) with Failure _ -> x;;
+
+(* ------------------------------------------------------------------------- *)
+(* Handy list operations.                                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec (--) = fun m n -> if m > n then [] else m::((m + 1) -- n);;
+
+let rec (---) = fun m n -> if m >/ n then [] else m::((m +/ Int 1) --- n);;
+
+let rec map2 f l1 l2 =
+  match (l1,l2) with
+    [],[] -> []
+  | (h1::t1),(h2::t2) -> let h = f h1 h2 in h::(map2 f t1 t2)
+  | _ -> failwith "map2: length mismatch";;
+
+let rev =
+  let rec rev_append acc l =
+    match l with
+      [] -> acc
+    | h::t -> rev_append (h::acc) t in
+  fun l -> rev_append [] l;;
+
+let hd l =
+  match l with
+   h::t -> h
+  | _ -> failwith "hd";;
+
+let tl l =
+  match l with
+   h::t -> t
+  | _ -> failwith "tl";;
+-}
+
+-- (^) = (++)
+
+itlist :: (a -> b -> b) -> [a] -> b -> b
+-- itlist f xs z = foldr f z xs
+itlist f xs z = foldr f z xs
+
+end_itlist :: (t -> t -> t) -> [t] -> t
+-- end_itlist = foldr1
+end_itlist = foldr1
+
+itlist2 :: (t -> t1 -> Failing t2 -> Failing t2) -> [t] -> [t1] -> Failing t2 -> Failing t2
+itlist2 f l1 l2 b =
+  case (l1,l2) of
+    ([],[]) -> b
+    (h1 : t1, h2 : t2) -> f h1 h2 (itlist2 f t1 t2 b)
+    _ -> Failure ["itlist2"]
+
+{-
+let rec zip l1 l2 =
+  match (l1,l2) with
+        ([],[]) -> []
+      | (h1::t1,h2::t2) -> (h1,h2)::(zip t1 t2)
+      | _ -> failwith "zip";;
+
+let rec forall p l =
+  match l with
+    [] -> true
+  | h::t -> p(h) & forall p t;;
+-}
+exists :: (a -> Bool) -> [a] -> Bool
+exists = any
+{-
+let partition p l =
+    itlist (fun a (yes,no) -> if p a then a::yes,no else yes,a::no) l ([],[]);;
+
+let filter p l = fst(partition p l);;
+
+let length =
+  let rec len k l =
+    if l = [] then k else len (k + 1) (tl l) in
+  fun l -> len 0 l;;
+
+let rec last l =
+  match l with
+    [x] -> x
+  | (h::t) -> last t
+  | [] -> failwith "last";;
+
+let rec butlast l =
+  match l with
+    [_] -> []
+  | (h::t) -> h::(butlast t)
+  | [] -> failwith "butlast";;
+
+let rec find p l =
+  match l with
+      [] -> failwith "find"
+    | (h::t) -> if p(h) then h else find p t;;
+
+let rec el n l =
+  if n = 0 then hd l else el (n - 1) (tl l);;
+
+let map f =
+  let rec mapf l =
+    match l with
+      [] -> []
+    | (x::t) -> let y = f x in y::(mapf t) in
+  mapf;;
+-}
+
+allpairs :: forall a b c. (Ord c) => (a -> b -> c) -> Set.Set a -> Set.Set b -> Set.Set c
+-- allpairs f xs ys = Set.fromList (concatMap (\ z -> map (f z) (Set.toList ys)) (Set.toList xs))
+allpairs f xs ys = Set.fold (\ x zs -> Set.fold (\ y zs' -> Set.insert (f x y) zs') zs ys) Set.empty xs
+
+distrib' :: Ord a => Set.Set (Set.Set a) -> Set.Set (Set.Set a) -> Set.Set (Set.Set a)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+test01 :: Test
+test01 = TestCase $ assertEqual "itlist2" expected input
+    where input = allpairs (,) (Set.fromList [1,2,3]) (Set.fromList [4,5,6])
+          expected = Set.fromList [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)] :: Set.Set (Int, Int)
+
+{-
+let rec distinctpairs l =
+  match l with
+   x::t -> itlist (fun y a -> (x,y) :: a) t (distinctpairs t)
+  | [] -> [];;
+
+let rec chop_list n l =
+  if n = 0 then [],l else
+  try let m,l' = chop_list (n-1) (tl l) in (hd l)::m,l'
+  with Failure _ -> failwith "chop_list";;
+
+let replicate n a = map (fun x -> a) (1--n);;
+
+let rec insertat i x l =
+  if i = 0 then x::l else
+  match l with
+    [] -> failwith "insertat: list too short for position to exist"
+  | h::t -> h::(insertat (i-1) x t);;
+
+let rec forall2 p l1 l2 =
+  match (l1,l2) with
+    [],[] -> true
+  | (h1::t1,h2::t2) -> p h1 h2 & forall2 p t1 t2
+  | _ -> false;;
+
+let index x =
+  let rec ind n l =
+    match l with
+      [] -> failwith "index"
+    | (h::t) -> if Pervasives.compare x h = 0 then n else ind (n + 1) t in
+  ind 0;;
+
+let rec unzip l =
+  match l with
+    [] -> [],[]
+  | (x,y)::t ->
+      let xs,ys = unzip t in x::xs,y::ys;;
+
+(* ------------------------------------------------------------------------- *)
+(* Whether the first of two items comes earlier in the list.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec earlier l x y =
+  match l with
+    h::t -> (Pervasives.compare h y <> 0) &
+            (Pervasives.compare h x = 0 or earlier t x y)
+  | [] -> false;;
+
+(* ------------------------------------------------------------------------- *)
+(* Application of (presumably imperative) function over a list.              *)
+(* ------------------------------------------------------------------------- *)
+
+let rec do_list f l =
+  match l with
+    [] -> ()
+  | h::t -> f(h); do_list f t;;
+
+(* ------------------------------------------------------------------------- *)
+(* Association lists.                                                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare x a = 0 then y else assoc a t
+  | [] -> failwith "find";;
+
+let rec rev_assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare y a = 0 then x else rev_assoc a t
+  | [] -> failwith "find";;
+
+(* ------------------------------------------------------------------------- *)
+(* Merging of sorted lists (maintaining repetitions).                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec merge ord l1 l2 =
+  match l1 with
+    [] -> l2
+  | h1::t1 -> match l2 with
+                [] -> l1
+              | h2::t2 -> if ord h1 h2 then h1::(merge ord t1 l2)
+                          else h2::(merge ord l1 t2);;
+
+(* ------------------------------------------------------------------------- *)
+(* Bottom-up mergesort.                                                      *)
+(* ------------------------------------------------------------------------- *)
+
+let sort ord =
+  let rec mergepairs l1 l2 =
+    match (l1,l2) with
+        ([s],[]) -> s
+      | (l,[]) -> mergepairs [] l
+      | (l,[s1]) -> mergepairs (s1::l) []
+      | (l,(s1::s2::ss)) -> mergepairs ((merge ord s1 s2)::l) ss in
+  fun l -> if l = [] then [] else mergepairs [] (map (fun x -> [x]) l);;
+
+(* ------------------------------------------------------------------------- *)
+(* Common measure predicates to use with "sort".                             *)
+(* ------------------------------------------------------------------------- *)
+
+let increasing f x y = Pervasives.compare (f x) (f y) < 0;;
+
+let decreasing f x y = Pervasives.compare (f x) (f y) > 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* Eliminate repetitions of adjacent elements, with and without counting.    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec uniq l =
+  match l with
+    x::(y::_ as t) -> let t' = uniq t in
+                      if Pervasives.compare x y = 0 then t' else
+                      if t'==t then l else x::t'
+ | _ -> l;;
+
+let repetitions =
+  let rec repcount n l =
+    match l with
+      x::(y::_ as ys) -> if Pervasives.compare y x = 0 then repcount (n + 1) ys
+                  else (x,n)::(repcount 1 ys)
+    | [x] -> [x,n]
+    | [] -> failwith "repcount" in
+  fun l -> if l = [] then [] else repcount 1 l;;
+-}
+
+tryfind :: (t -> Failing a) -> [t] -> Failing a
+tryfind _ [] = Failure ["tryfind"]
+tryfind f l =
+    case l of
+      [] -> Failure ["tryfind"]
+      h : t -> failing (\ _ -> tryfind f t) Success (f h)
+
+settryfind :: (t -> Failing a) -> Set.Set t -> Failing a
+settryfind f l =
+    case Set.minView l of
+      Nothing -> Failure ["settryfind"]
+      Just (h, t) -> failing (\ _ -> settryfind f t) Success (f h)
+
+mapfilter :: (a -> Failing b) -> [a] -> [b]
+mapfilter f l = catMaybes (map (failing (const Nothing) Just . f) l) 
+    -- filter (failing (const False) (const True)) (map f l)
+
+setmapfilter :: Ord b => (a -> Failing b) -> Set.Set a -> Set.Set b
+setmapfilter f s = Set.fold (\ a r -> failing (const r) (`Set.insert` r) (f a)) Set.empty s
+
+-- -------------------------------------------------------------------------
+-- Find list member that maximizes or minimizes a function.                 
+-- -------------------------------------------------------------------------
+
+optimize :: forall a b. (b -> b -> Bool) -> (a -> b) -> [a] -> Maybe a
+optimize _ _ [] = Nothing
+optimize ord f l = Just (fst (foldr1 (\ p@(_,y) p'@(_,y') -> if ord y y' then p else p') (map (\ x -> (x,f x)) l)))
+
+maximize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+maximize f l = optimize (>) f l
+
+minimize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+minimize f l = optimize (<) f l
+
+optimize' :: forall a b. (b -> b -> Bool) -> (a -> b) -> Set.Set a -> Maybe a
+optimize' ord f s = optimize ord f (Set.toList s)
+
+maximize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+maximize' f s = optimize' (>) f s
+
+minimize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+minimize' f s = optimize' (<) f s
+
+-- -------------------------------------------------------------------------
+-- Set operations on ordered lists.                                         
+-- -------------------------------------------------------------------------
+{-
+let setify =
+  let rec canonical lis =
+     match lis with
+       x::(y::_ as rest) -> Pervasives.compare x y < 0 & canonical rest
+     | _ -> true in
+  fun l -> if canonical l then l
+           else uniq (sort (fun x y -> Pervasives.compare x y <= 0) l);;
+
+let union =
+  let rec union l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> l2
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(union t1 t2)
+          else if h1 < h2 then h1::(union t1 l2)
+          else h2::(union l1 t2) in
+  fun s1 s2 -> union (setify s1) (setify s2);;
+
+let intersect =
+  let rec intersect l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> []
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(intersect t1 t2)
+          else if h1 < h2 then intersect t1 l2
+          else intersect l1 t2 in
+  fun s1 s2 -> intersect (setify s1) (setify s2);;
+
+let subtract =
+  let rec subtract l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then subtract t1 t2
+          else if h1 < h2 then h1::(subtract t1 l2)
+          else subtract l1 t2 in
+  fun s1 s2 -> subtract (setify s1) (setify s2);;
+
+let subset,psubset =
+  let rec subset l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> true
+      | (l1,[]) -> false
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then subset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2
+  and psubset l1 l2 =
+    match (l1,l2) with
+        (l1,[]) -> false
+      | ([],l2) -> true
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then psubset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2 in
+  (fun s1 s2 -> subset (setify s1) (setify s2)),
+  (fun s1 s2 -> psubset (setify s1) (setify s2));;
+
+let rec set_eq s1 s2 = (setify s1 = setify s2);;
+
+let insert x s = union [x] s;;
+-}
+
+image :: (Ord b, Ord a) => (a -> b) -> Set.Set a -> Set.Set b
+image f s = Set.map f s
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Union of a family of sets.                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let unions s = setify(itlist (@) s []);;
+
+(* ------------------------------------------------------------------------- *)
+(* List membership. This does *not* assume the list is a set.                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec mem x lis =
+  match lis with
+    [] -> false
+  | (h::t) -> Pervasives.compare x h = 0 or mem x t;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Finding all subsets or all subsets of a given size.                       
+-- ------------------------------------------------------------------------- 
+
+-- allsets :: Ord a => Int -> Set.Set a -> Set.Set (Set.Set a)
+allsets :: forall a b. (Num a, Eq a, Ord b) => a -> Set.Set b -> Set.Set (Set.Set b)
+allsets 0 _ = Set.singleton Set.empty
+allsets m l =
+    case Set.minView l of
+      Nothing -> Set.empty
+      Just (h, t) -> Set.union (Set.map (Set.insert h) (allsets (m - 1) t)) (allsets m t)
+
+allsubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allsubsets s =
+    maybe (Set.singleton Set.empty)
+          (\ (x, t) -> 
+               let res = allsubsets t in
+               Set.union res (Set.map (Set.insert x) res))
+          (Set.minView s)
+
+
+allnonemptysubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allnonemptysubsets s = Set.delete Set.empty (allsubsets s)
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Explosion and implosion of strings.                                       *)
+(* ------------------------------------------------------------------------- *)
+
+let explode s =
+  let rec exap n l =
+     if n < 0 then l else
+      exap (n - 1) ((String.sub s n 1)::l) in
+  exap (String.length s - 1) [];;
+
+let implode l = itlist (^) l "";;
+
+(* ------------------------------------------------------------------------- *)
+(* Timing; useful for documentation but not logically necessary.             *)
+(* ------------------------------------------------------------------------- *)
+
+let time f x =
+  let start_time = Sys.time() in
+  let result = f x in
+  let finish_time = Sys.time() in
+  print_string
+    ("CPU time (user): "^(string_of_float(finish_time -. start_time)));
+  print_newline();
+  result;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Polymorphic finite partial functions via Patricia trees.                 
+--                                                                          
+-- The point of this strange representation is that it is canonical (equal  
+-- functions have the same encoding) yet reasonably efficient on average.   
+--                                                                          
+-- Idea due to Diego Olivier Fernandez Pons (OCaml list, 2003/11/10).       
+-- -------------------------------------------------------------------------
+{-
+data Func a b
+    = Empty
+    | Leaf Int [(a, b)]
+    | Branch Int Int (Func a b) (Func a b)
+
+-- -------------------------------------------------------------------------
+-- Undefined function.                                                      
+-- -------------------------------------------------------------------------
+
+undefinedFunction = Empty
+
+-- -------------------------------------------------------------------------
+-- In case of equality comparison worries, better use this.                 
+-- -------------------------------------------------------------------------
+
+isUndefined Empty = True
+isUndefined _ = False
+
+-- -------------------------------------------------------------------------
+-- Operation analogous to "map" for functions.                                  
+-- -------------------------------------------------------------------------
+
+mapf f t =
+    case t of
+      Empty -> Empty
+      Leaf h l -> Leaf h (map_list f l)
+      Branch p b l r -> Branch p b (mapf f l) (mapf f r)
+    where
+      map_list f l =
+          case l of
+            [] -> []
+            (x,y) : t -> (x, f y) : map_list f t
+
+-- -------------------------------------------------------------------------
+-- Operations analogous to "fold" for lists.                                
+-- -------------------------------------------------------------------------
+
+foldlFn f a t =
+    case t of
+      Empty -> a
+      Leaf h l -> foldl_list f a l
+      Branch p b l r -> foldlFn f (foldlFn f a l) r
+    where
+      foldl_list f a l =
+          case l of
+            [] -> a
+            (x,y) : t -> foldl_list f (f a x y) t
+
+foldrFn f t a =
+    case t of
+      Empty -> a
+      Leaf h l -> foldr_list f l a
+      Branch p b l r -> foldrFn f l (foldrFn f r a)
+    where
+      foldr_list f l a =
+          case l of
+            [] -> a
+            (x, y) : t -> f x y (foldr_list f t a)
+
+-- -------------------------------------------------------------------------
+-- Mapping to sorted-list representation of the graph, domain and range.    
+-- -------------------------------------------------------------------------
+
+graph f = Set.fromList (foldlFn (\ a x y -> (x,y) : a) [] f)
+
+dom f = Set.fromList (foldlFn (\ a x y -> x :a) [] f)
+
+ran f = Set.fromList (foldlFn (\ a x y -> y : a) [] f)
+-}
+
+-- -------------------------------------------------------------------------
+-- Application.                                                             
+-- -------------------------------------------------------------------------
+
+applyD :: Ord k => Map.Map k a -> k -> a -> Map.Map k a
+applyD m k a = Map.insert k a m
+
+apply :: Ord k => Map.Map k a -> k -> Maybe a
+apply m k = Map.lookup k m
+
+tryApplyD :: Ord k => Map.Map k a -> k -> a -> a
+tryApplyD m k d = fromMaybe d (Map.lookup k m)
+
+tryApplyL :: Ord k => Map.Map k [a] -> k -> [a]
+tryApplyL m k = tryApplyD m k []
+{-
+applyD :: (t -> Maybe b) -> (t -> b) -> t -> b
+applyD f d x = maybe (d x) id (f x)
+
+apply :: (t -> Maybe b) -> t -> b
+apply f = applyD f (\ _ -> error "apply")
+
+tryApplyD :: (t -> Maybe b) -> t -> b -> b
+tryApplyD f a d = maybe d id (f a)
+
+tryApplyL :: (t -> Maybe [a]) -> t -> [a]
+tryApplyL f x = tryApplyD f x []
+-}
+
+defined :: Ord t => Map.Map t a -> t -> Bool
+defined = flip Map.member
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Undefinition.                                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let undefine =
+  let rec undefine_list x l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then t
+          else if c < 0 then l else
+          let t' = undefine_list x t in
+          if t' == t then l else ab::t'
+    | [] -> [] in
+  fun x ->
+    let k = Hashtbl.hash x in
+    let rec und t =
+      match t with
+        Leaf(h,l) when h = k ->
+          let l' = undefine_list x l in
+          if l' == l then t
+          else if l' = [] then Empty
+          else Leaf(h,l')
+      | Branch(p,b,l,r) when k land (b - 1) = p ->
+          if k land b = 0 then
+            let l' = und l in
+            if l' == l then t
+            else (match l' with Empty -> r | _ -> Branch(p,b,l',r))
+          else
+            let r' = und r in
+            if r' == r then t
+            else (match r' with Empty -> l | _ -> Branch(p,b,l,r'))
+      | _ -> t in
+    und;;
+
+(* ------------------------------------------------------------------------- *)
+(* Redefinition and combination.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let (|->),combine =
+  let newbranch p1 t1 p2 t2 =
+    let zp = p1 lxor p2 in
+    let b = zp land (-zp) in
+    let p = p1 land (b - 1) in
+    if p1 land b = 0 then Branch(p,b,t1,t2)
+    else Branch(p,b,t2,t1) in
+  let rec define_list (x,y as xy) l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then xy::t
+          else if c < 0 then xy::l
+          else ab::(define_list xy t)
+    | [] -> [xy]
+  and combine_list op z l1 l2 =
+    match (l1,l2) with
+      [],_ -> l2
+    | _,[] -> l1
+    | ((x1,y1 as xy1)::t1,(x2,y2 as xy2)::t2) ->
+          let c = Pervasives.compare x1 x2 in
+          if c < 0 then xy1::(combine_list op z t1 l2)
+          else if c > 0 then xy2::(combine_list op z l1 t2) else
+          let y = op y1 y2 and l = combine_list op z t1 t2 in
+          if z(y) then l else (x1,y)::l in
+  let (|->) x y =
+    let k = Hashtbl.hash x in
+    let rec upd t =
+      match t with
+        Empty -> Leaf (k,[x,y])
+      | Leaf(h,l) ->
+           if h = k then Leaf(h,define_list (x,y) l)
+           else newbranch h t k (Leaf(k,[x,y]))
+      | Branch(p,b,l,r) ->
+          if k land (b - 1) <> p then newbranch p t k (Leaf(k,[x,y]))
+          else if k land b = 0 then Branch(p,b,upd l,r)
+          else Branch(p,b,l,upd r) in
+    upd in
+  let rec combine op z t1 t2 =
+    match (t1,t2) with
+      Empty,_ -> t2
+    | _,Empty -> t1
+    | Leaf(h1,l1),Leaf(h2,l2) ->
+          if h1 = h2 then
+            let l = combine_list op z l1 l2 in
+            if l = [] then Empty else Leaf(h1,l)
+          else newbranch h1 t1 h2 t2
+    | (Leaf(k,lis) as lf),(Branch(p,b,l,r) as br) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z lf l with
+                 Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z lf r with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch k lf p br
+    | (Branch(p,b,l,r) as br),(Leaf(k,lis) as lf) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z l lf with
+                Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z r lf with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch p br k lf
+    | Branch(p1,b1,l1,r1),Branch(p2,b2,l2,r2) ->
+          if b1 < b2 then
+            if p2 land (b1 - 1) <> p1 then newbranch p1 t1 p2 t2
+            else if p2 land b1 = 0 then
+              (match combine op z l1 t2 with
+                 Empty -> r1 | l -> Branch(p1,b1,l,r1))
+            else
+              (match combine op z r1 t2 with
+                 Empty -> l1 | r -> Branch(p1,b1,l1,r))
+          else if b2 < b1 then
+            if p1 land (b2 - 1) <> p2 then newbranch p1 t1 p2 t2
+            else if p1 land b2 = 0 then
+              (match combine op z t1 l2 with
+                 Empty -> r2 | l -> Branch(p2,b2,l,r2))
+            else
+              (match combine op z t1 r2 with
+                 Empty -> l2 | r -> Branch(p2,b2,l2,r))
+          else if p1 = p2 then
+           (match (combine op z l1 l2,combine op z r1 r2) with
+              (Empty,r) -> r | (l,Empty) -> l | (l,r) -> Branch(p1,b1,l,r))
+          else
+            newbranch p1 t1 p2 t2 in
+  (|->),combine;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Special case of point function.                                          
+-- -------------------------------------------------------------------------
+
+(|=>) :: Ord k => k -> a -> Map.Map k a
+x |=> y = Map.fromList [(x, y)]
+
+-- -------------------------------------------------------------------------
+-- Idiom for a mapping zipping domain and range lists.                      
+-- -------------------------------------------------------------------------
+
+(|->) :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
+(|->) a b m = Map.insert a b m
+
+fpf :: Ord a => Map.Map a b -> a -> Maybe b
+fpf m a = Map.lookup a m
+
+-- -------------------------------------------------------------------------
+-- Grab an arbitrary element.                                               
+-- -------------------------------------------------------------------------
+
+choose :: Map.Map k a -> (k, a)
+choose = Map.findMin
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Install a (trivial) printer for finite partial functions.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let print_fpf (f:('a,'b)func) = print_string "<func>";;
+
+#install_printer print_fpf;;
+
+(* ------------------------------------------------------------------------- *)
+(* Related stuff for standard functions.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let valmod a y f x = if x = a then y else f(x);;
+
+let undef x = failwith "undefined function";;
+
+(* ------------------------------------------------------------------------- *)
+(* Union-find algorithm.                                                     *)
+(* ------------------------------------------------------------------------- *)
+
+type ('a)pnode = Nonterminal of 'a | Terminal of 'a * int;;
+
+type ('a)partition = Partition of ('a,('a)pnode)func;;
+
+let rec terminus (Partition f as ptn) a =
+  match (apply f a) with
+    Nonterminal(b) -> terminus ptn b
+  | Terminal(p,q) -> (p,q);;
+
+let tryterminus ptn a =
+  try terminus ptn a with Failure _ -> (a,1);;
+
+let canonize ptn a = fst(tryterminus ptn a);;
+
+let equivalent eqv a b = canonize eqv a = canonize eqv b;;
+
+let equate (a,b) (Partition f as ptn) =
+  let (a',na) = tryterminus ptn a
+  and (b',nb) = tryterminus ptn b in
+  Partition
+   (if a' = b' then f else
+    if na <= nb then
+       itlist identity [a' |-> Nonterminal b'; b' |-> Terminal(b',na+nb)] f
+    else
+       itlist identity [b' |-> Nonterminal a'; a' |-> Terminal(a',na+nb)] f);;
+
+let unequal = Partition undefined;;
+
+let equated (Partition f) = dom f;;
+
+(* ------------------------------------------------------------------------- *)
+(* First number starting at n for which p succeeds.                          *)
+(* ------------------------------------------------------------------------- *)
+
+let rec first n p = if p(n) then n else first (n +/ Int 1) p;;
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/Meson.hs b/testdata/logic/Data/Logic/Harrison/Meson.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Meson.hs
@@ -0,0 +1,547 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Meson where
+
+import Control.Applicative.Error (Failing(..))
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (Constants, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), negative)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.FOL (generalize, list_conj)
+import Data.Logic.Harrison.Lib (setAll, settryfind)
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf)
+import Data.Logic.Harrison.Prolog (renamerule)
+import Data.Logic.Harrison.Skolem (SkolemT, pnf, specialize, askolemize)
+import Data.Logic.Harrison.Tableaux (unify_literals, deepen)
+
+-- =========================================================================
+-- Model elimination procedure (MESON version, based on Stickel's PTTP).     
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Example of naivety of tableau prover.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+tab <<forall a. ~(P(a) /\ (forall y z. Q(y) \/ R(z)) /\ ~P(a))>>;;
+
+tab <<forall a. ~(P(a) /\ ~P(a) /\ (forall y z. Q(y) \/ R(z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The interesting example where tableaux connections make the proof longer. 
+-- Unfortuntely this gets hammered by normalization first...                 
+-- ------------------------------------------------------------------------- 
+
+tab <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+      (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+      (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Generation of contrapositives.                                            
+-- ------------------------------------------------------------------------- 
+
+contrapositives :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => Set.Set fof -> Set.Set (Set.Set fof, fof)
+contrapositives cls =
+    if setAll negative cls then Set.insert (Set.map (.~.) cls,false) base else base
+    where base = Set.map (\ c -> (Set.map (.~.) (Set.delete c cls), c)) cls
+
+-- ------------------------------------------------------------------------- 
+-- The core of MESON: ancestor unification or Prolog-style extension.        
+-- ------------------------------------------------------------------------- 
+
+mexpand :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set (Set.Set fof, fof)
+        -> Set.Set fof
+        -> fof
+        -> ((Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int))
+        -> (Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int)
+mexpand rules ancestors g cont (env,n,k) =
+    if n < 0
+    then Failure ["Too deep"]
+    else case settryfind doAncestor ancestors of
+           Success a -> Success a
+           Failure _ -> settryfind doRule rules
+    where
+      doAncestor a =
+          do mp <- unify_literals env g ((.~.) a)
+             cont (mp, n, k)
+      doRule rule =
+          do mp <- unify_literals env g c
+             mexpand' (mp, n - Set.size asm, k')
+          where
+            mexpand' = Set.fold (mexpand rules (Set.insert g ancestors)) cont asm
+            ((asm, c), k') = renamerule k rule
+
+-- ------------------------------------------------------------------------- 
+-- Full MESON procedure.                                                     
+-- ------------------------------------------------------------------------- 
+
+puremeson :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+             Maybe Int -> fof -> Failing ((Map.Map v term, Int, Int), Int)
+puremeson maxdl fm =
+    deepen f 0 maxdl
+    where
+      f n = mexpand rules Set.empty false return (Map.empty, n, 0)
+      rules = Set.fold (Set.union . contrapositives) Set.empty cls
+      cls = simpcnf (specialize (pnf fm))
+
+meson :: forall m fof atom term f v. (FirstOrderFormula fof atom v, PropositionalFormula fof atom, Literal fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+         Maybe Int -> fof -> SkolemT v term m (Set.Set (Failing ((Map.Map v term, Int, Int), Int)))
+meson maxdl fm =
+    askolemize ((.~.)(generalize fm)) >>=
+    return . Set.map (puremeson maxdl . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+{-
+-- ------------------------------------------------------------------------- 
+-- With repetition checking and divide-and-conquer search.                   
+-- ------------------------------------------------------------------------- 
+
+let rec equal env fm1 fm2 =
+  try unify_literals env (fm1,fm2) == env with Failure _ -> false;;
+
+let expand2 expfn goals1 n1 goals2 n2 n3 cont env k =
+   expfn goals1 (fun (e1,r1,k1) ->
+        expfn goals2 (fun (e2,r2,k2) ->
+                        if n2 + r1 <= n3 + r2 then failwith "pair"
+                        else cont(e2,r2,k2))
+              (e1,n2+r1,k1))
+        (env,n1,k);;
+
+let rec mexpand rules ancestors g cont (env,n,k) =
+  if n < 0 then failwith "Too deep"
+  else if exists (equal env g) ancestors then failwith "repetition" else
+  try tryfind (fun a -> cont (unify_literals env (g,negate a),n,k))
+              ancestors
+  with Failure _ -> tryfind
+    (fun r -> let (asm,c),k' = renamerule k r in
+              mexpands rules (g::ancestors) asm cont
+                       (unify_literals env (g,c),n-length asm,k'))
+    rules
+
+and mexpands rules ancestors gs cont (env,n,k) =
+  if n < 0 then failwith "Too deep" else
+  let m = length gs in
+  if m <= 1 then itlist (mexpand rules ancestors) gs cont (env,n,k) else
+  let n1 = n / 2 in
+  let n2 = n - n1 in
+  let goals1,goals2 = chop_list (m / 2) gs in
+  let expfn = expand2 (mexpands rules ancestors) in
+  try expfn goals1 n1 goals2 n2 (-1) cont env k
+  with Failure _ -> expfn goals2 n1 goals1 n2 n1 cont env k;;
+
+let puremeson fm =
+  let cls = simpcnf(specialize(pnf fm)) in
+  let rules = itlist ((@) ** contrapositives) cls [] in
+  deepen (fun n ->
+     mexpand rules [] False (fun x -> x) (undefined,n,0); n) 0;;
+
+let meson fm =
+  let fm1 = askolemize(Not(generalize fm)) in
+  map (puremeson ** list_conj) (simpdnf fm1);;
+
+-- ------------------------------------------------------------------------- 
+-- The Los problem (depth 20) and the Steamroller (depth 53) --- lengthier.  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+{- ***********
+
+let los = meson
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+let steamroller = meson
+ <<((forall x. P1(x) ==> P0(x)) /\ (exists x. P1(x))) /\
+   ((forall x. P2(x) ==> P0(x)) /\ (exists x. P2(x))) /\
+   ((forall x. P3(x) ==> P0(x)) /\ (exists x. P3(x))) /\
+   ((forall x. P4(x) ==> P0(x)) /\ (exists x. P4(x))) /\
+   ((forall x. P5(x) ==> P0(x)) /\ (exists x. P5(x))) /\
+   ((exists x. Q1(x)) /\ (forall x. Q1(x) ==> Q0(x))) /\
+   (forall x. P0(x)
+              ==> (forall y. Q0(y) ==> R(x,y)) \/
+                  ((forall y. P0(y) /\ S0(y,x) /\
+                              (exists z. Q0(z) /\ R(y,z))
+                              ==> R(x,y)))) /\
+   (forall x y. P3(y) /\ (P5(x) \/ P4(x)) ==> S0(x,y)) /\
+   (forall x y. P3(x) /\ P2(y) ==> S0(x,y)) /\
+   (forall x y. P2(x) /\ P1(y) ==> S0(x,y)) /\
+   (forall x y. P1(x) /\ (P2(y) \/ Q1(y)) ==> ~(R(x,y))) /\
+   (forall x y. P3(x) /\ P4(y) ==> R(x,y)) /\
+   (forall x y. P3(x) /\ P5(y) ==> ~(R(x,y))) /\
+   (forall x. (P4(x) \/ P5(x)) ==> exists y. Q0(y) /\ R(x,y))
+   ==> exists x y. P0(x) /\ P0(y) /\
+                   exists z. Q1(z) /\ R(y,z) /\ R(x,y)>>;;
+
+*************** -}
+
+
+-- ------------------------------------------------------------------------- 
+-- Test it.                                                                  
+-- ------------------------------------------------------------------------- 
+
+let prop_1 = time meson
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let prop_2 = time meson
+ <<~ ~p <=> p>>;;
+
+let prop_3 = time meson
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let prop_4 = time meson
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let prop_5 = time meson
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let prop_6 = time meson
+ <<p \/ ~p>>;;
+
+let prop_7 = time meson
+ <<p \/ ~ ~ ~p>>;;
+
+let prop_8 = time meson
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let prop_9 = time meson
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let prop_10 = time meson
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let prop_11 = time meson
+ <<p <=> p>>;;
+
+let prop_12 = time meson
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let prop_13 = time meson
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let prop_14 = time meson
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let prop_15 = time meson
+ <<p ==> q <=> ~p \/ q>>;;
+
+let prop_16 = time meson
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let prop_17 = time meson
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time meson
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time meson
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time meson
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time meson
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time meson
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time meson
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time meson
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time meson
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time meson
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time meson
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time meson
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time meson
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time meson
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time meson
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time meson
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time meson
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time meson
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time meson
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time meson
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time meson
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time meson
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+let p39 = time meson
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time meson
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time meson
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time meson
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+let p43 = time meson
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+let p44 = time meson
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+let p45 = time meson
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+let p46 = time meson
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time meson
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time meson
+ <<P(f((a),b),f(b,c)) /\
+  P(f(b,c),f(a,c)) /\
+  (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+  ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time meson
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time meson
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time meson
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- ** Amazingly, this still seems non-trivial... in HOL it works at depth 45!
+
+let gilmore_1 = time meson
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+ ** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time meson
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time meson
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time meson
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time meson
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time meson
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This is still a very hard problem
+
+let gilmore_9 = time meson
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Translation of Gilmore procedure using separate definitions.              
+-- ------------------------------------------------------------------------- 
+
+let gilmore_9a = time meson
+ <<(forall x y. P(x,y) <=>
+                forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+   ==> forall x. exists y. forall z.
+             (P(y,x) ==> (P(x,z) ==> P(x,y))) /\
+             (P(x,y) ==> (~P(x,z) ==> P(y,x) /\ P(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time meson
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The "connections make things worse" example once again.                   
+-- ------------------------------------------------------------------------- 
+
+meson <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+        (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+        (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/Normal.hs b/testdata/logic/Data/Logic/Harrison/Normal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Normal.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Versions of the normal form functions in Prop for FirstOrderFormula.
+module Data.Logic.Harrison.Normal
+    ( trivial
+    , simpdnf
+    , simpdnf'
+    , simpcnf
+    , simpcnf'
+    ) where
+
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal, fromFirstOrder)
+import Data.Logic.Classes.Negate (Negatable, negated, (.~.))
+import Data.Logic.Failing (failing)
+import Data.Logic.Harrison.Lib (setAny, allpairs)
+import Data.Logic.Harrison.Skolem (nnf)
+import qualified Data.Set.Extra as Set
+import Prelude hiding (negate)
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.  (dsf: now set)
+-- ------------------------------------------------------------------------- 
+
+distrib' :: (Eq formula, Ord formula) => Set.Set (Set.Set formula) -> Set.Set (Set.Set formula) -> Set.Set (Set.Set formula)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (neg, pos) = Set.partition negated lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: (FirstOrderFormula fof atom v, Eq fof, Ord fof) =>
+           fof -> Set.Set (Set.Set fof)
+simpdnf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at _ = Set.singleton (Set.singleton fm)
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf (nnf fm))
+
+purednf :: (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purednf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = Set.singleton (Set.singleton fm)
+      co (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      co (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      co _ = Set.singleton (Set.singleton fm)
+      tf = Set.singleton . Set.singleton . fromBool
+      at _ = Set.singleton (Set.singleton fm)
+
+simpdnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Formula lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+simpdnf' fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at = Set.singleton . Set.singleton . atomic
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf' (nnf fm))
+
+purednf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+purednf' fm =
+    foldFirstOrder (\ _ _ _ -> x) co (\ _ -> x) (\ _ -> x)  fm
+    where
+      -- co :: Combination formula -> Set.Set (Set.Set lit)
+      co (BinOp p (:&:) q) = Set.distrib (purednf' p) (purednf' q)
+      co (BinOp p (:|:) q) = Set.union (purednf' p) (purednf' q)
+      co _ = x
+      -- x :: Set.Set (Set.Set lit)
+      x = failing (const (error "purednf'")) (Set.singleton . Set.singleton) (fromFirstOrder id fm)
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+-- It would be nice to share code this way, but the caller needs to
+-- specify the intermediate lit type, which is a pain.
+-- simpcnf :: forall fof lit atom v. (FirstOrderFormula fof atom v, Ord fof, Literal lit atom v, Eq lit, Ord lit) => fof -> Set.Set (Set.Set fof)
+-- simpcnf fm = Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+
+simpcnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+simpcnf fm =
+    -- Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      at x = Set.singleton (Set.singleton (atomic x))
+      -- Discard any clause that is the proper subset of another clause
+      def = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+
+purecnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purecnf fm = Set.map (Set.map ({-simplify .-} (.~.))) (purednf (nnf ((.~.) fm)))
+
+-- Alternative versions, these should be merged
+
+simpcnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+simpcnf' fm =
+    foldFirstOrder (\ _ _ _ -> cjs') co tf at fm
+    where
+      co _ = cjs'
+      at = Set.singleton . Set.singleton . atomic -- foldAtomEq (\ _ _ -> cjs') tf (\ _ _ -> cjs')
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (`Set.isProperSubsetOf` x) cjs))
+      cjs = Set.filter (not . trivial) (purecnf' (nnf fm)) -- :: Set.Set (Set.Set lit)
+
+-- | CNF: (a | b | c) & (d | e | f)
+purecnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+purecnf' fm = Set.map (Set.map (.~.)) (purednf' (nnf ((.~.) fm)))
diff --git a/testdata/logic/Data/Logic/Harrison/Prolog.hs b/testdata/logic/Data/Logic/Harrison/Prolog.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Prolog.hs
@@ -0,0 +1,194 @@
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Prolog where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Term (Term(vt))
+import Data.String (IsString (fromString))
+import Data.Logic.Harrison.FOL (fv, subst, list_conj)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- ========================================================================= 
+-- Backchaining procedure for Horn clauses, and toy Prolog implementation.   
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Rename a rule.                                                            
+-- ------------------------------------------------------------------------- 
+
+renamerule :: forall fof atom term v f. (FirstOrderFormula fof atom v, {-Formula fof term v,-} Atom atom term v, Term term v f, Ord fof) =>
+              Int -> (Set.Set fof, fof) -> ((Set.Set fof, fof), Int)
+renamerule k (asm,c) =
+    ((Set.map inst asm, inst c), k + Set.size fvs)
+    where
+      fvs = fv (list_conj (Set.insert c asm)) :: Set.Set v
+      vvs = Map.fromList (map (\ (v, i) -> (v, vt (fromString ("_" ++ show i)))) (zip (Set.toList fvs) [k..])) :: Map.Map v term
+      inst = subst vvs :: fof -> fof
+
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Basic prover for Horn clauses based on backchaining with unification.     *)
+(* ------------------------------------------------------------------------- *)
+
+let rec backchain rules n k env goals =
+  match goals with
+    [] -> env
+  | g::gs ->
+     if n = 0 then failwith "Too deep" else
+     tryfind (fun rule ->
+        let (a,c),k' = renamerule k rule in
+        backchain rules (n - 1) k' (unify_literals env (c,g)) (a @ gs))
+     rules;;
+
+let hornify cls =
+  let pos,neg = partition positive cls in
+  if length pos > 1 then failwith "non-Horn clause"
+  else (map negate neg,if pos = [] then False else hd pos);;
+
+let hornprove fm =
+  let rules = map hornify (simpcnf(skolemize(Not(generalize fm)))) in
+  deepen (fun n -> backchain rules n 0 undefined [False],n) 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* A Horn example.                                                           *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let p32 = hornprove
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x))
+   ==> (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* A non-Horn example.                                                       *)
+(* ------------------------------------------------------------------------- *)
+
+(****************
+
+hornprove <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+**********)
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* Parsing rules in a Prolog-like syntax.                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let parserule s =
+  let c,rest =
+    parse_formula (parse_infix_atom,parse_atom) [] (lex(explode s)) in
+  let asm,rest1 =
+    if rest <> [] & hd rest = ":-"
+    then parse_list ","
+          (parse_formula (parse_infix_atom,parse_atom) []) (tl rest)
+    else [],rest in
+  if rest1 = [] then (asm,c) else failwith "Extra material after rule";;
+
+(* ------------------------------------------------------------------------- *)
+(* Prolog interpreter: just use depth-first search not iterative deepening.  *)
+(* ------------------------------------------------------------------------- *)
+
+let simpleprolog rules gl =
+  backchain (map parserule rules) (-1) 0 undefined [parse gl];;
+
+(* ------------------------------------------------------------------------- *)
+(* Ordering example.                                                         *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let lerules = ["0 <= X"; "S(X) <= S(Y) :- X <= Y"];;
+
+simpleprolog lerules "S(S(0)) <= S(S(S(0)))";;
+
+(*** simpleprolog lerules "S(S(0)) <= S(0)";;
+ ***)
+
+let env = simpleprolog lerules "S(S(0)) <= X";;
+apply env "X";;
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* With instantiation collection to produce a more readable result.          *)
+(* ------------------------------------------------------------------------- *)
+
+let prolog rules gl =
+  let i = solve(simpleprolog rules gl) in
+  mapfilter (fun x -> Atom(R("=",[Var x; apply i x]))) (fv(parse gl));;
+
+(* ------------------------------------------------------------------------- *)
+(* Example again.                                                            *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+prolog lerules "S(S(0)) <= X";;
+
+(* ------------------------------------------------------------------------- *)
+(* Append example, showing symmetry between inputs and outputs.              *)
+(* ------------------------------------------------------------------------- *)
+
+let appendrules =
+  ["append(nil,L,L)"; "append(H::T,L,H::A) :- append(T,L,A)"];;
+
+prolog appendrules "append(1::2::nil,3::4::nil,Z)";;
+
+prolog appendrules "append(1::2::nil,Y,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,3::4::nil,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,Y,1::2::3::4::nil)";;
+
+(* ------------------------------------------------------------------------- *)
+(* However this way round doesn't work.                                      *)
+(* ------------------------------------------------------------------------- *)
+
+(***
+ *** prolog appendrules "append(X,3::4::nil,X)";;
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* A sorting example (from Lloyd's "Foundations of Logic Programming").      *)
+(* ------------------------------------------------------------------------- *)
+
+let sortrules =
+ ["sort(X,Y) :- perm(X,Y),sorted(Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+prolog sortrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+(* ------------------------------------------------------------------------- *)
+(* Yet with a simple swap of the first two predicates...                     *)
+(* ------------------------------------------------------------------------- *)
+
+let badrules =
+ ["sort(X,Y) :- sorted(Y), perm(X,Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+(*** This no longer works
+
+prolog badrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+ ***)
+END_INTERACTIVE;;                           
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/Prop.hs b/testdata/logic/Data/Logic/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Prop.hs
@@ -0,0 +1,450 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Prop
+    ( eval
+    , atoms
+    , onAllValuations
+    , TruthTable
+    , TruthTableRow
+    , truthTable
+    , tautology
+    , unsatisfiable
+    , satisfiable
+    , rawdnf
+    , purednf
+    , dnf
+    , dnf'
+    , trivial
+    , psimplify
+    , nnf
+    , simpdnf
+    , simpcnf
+    , positive
+    , negative
+    , negate
+    , distrib
+    , list_disj
+    , list_conj
+    -- previously unexported
+    , pSubst
+    , dual
+    , nenf
+    , mkLits
+    , allSatValuations
+    , dnf0
+    , cnf
+    , cnf'
+    ) where
+
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), true, false, ifElse)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal(foldLiteral), toPropositional)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional
+import Data.Logic.Harrison.Formulas.Propositional (atom_union, on_atoms)
+import Data.Logic.Harrison.Lib (fpf, setAny, distrib')
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Prelude hiding (negate)
+
+-- type Map a = Map.Map a Bool
+-- m0 = Map.empty
+-- ins :: forall a. Ord a => a -> Bool -> Map a -> Map a
+-- ins = Map.insert
+-- m ! k = Map.findWithDefault False k m
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of propositional formulas.                                        
+-- ------------------------------------------------------------------------- 
+
+{-
+let parse_propvar vs inp =
+  match inp with
+    p::oinp when p /= "(" -> Atom(P(p)),oinp
+  | _ -> failwith "parse_propvar";;
+
+let parse_prop_formula = make_parser
+  (parse_formula ((fun _ _ -> failwith ""),parse_propvar) []);;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Set this up as default for quotations.                                    
+-- ------------------------------------------------------------------------- 
+
+{-
+let default_parser = parse_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printer.                                                                  
+-- ------------------------------------------------------------------------- 
+
+{-
+let print_propvar prec p = print_string(pname p);;
+
+let print_prop_formula = print_qformula print_propvar;;
+
+#install_printer print_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Map.Map atomic Bool -> Bool
+eval fm v =
+    foldPropositional co id at fm
+    where
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at x = Map.findWithDefault False x v
+
+{-
+START_INTERACTIVE;;
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> false | P"r" -> true);;
+
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> true | P"r" -> false);;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Return the set of propositional variables in a formula.                   
+-- ------------------------------------------------------------------------- 
+
+atoms :: Ord atomic => PropositionalFormula formula atomic => formula -> Set.Set atomic
+atoms = atom_union Set.singleton
+
+-- ------------------------------------------------------------------------- 
+-- Code to print out truth tables.                                           
+-- ------------------------------------------------------------------------- 
+
+onAllValuations :: (Ord a) =>
+                   (r -> r -> r)         -- ^ Combine function for result type
+                -> (Map.Map a Bool -> r) -- ^ The substitution function
+                -> Map.Map a Bool        -- ^ The default valuation function for atoms not in ps
+                -> Set.Set a             -- ^ The variables to vary
+                -> r
+onAllValuations _ subfn v ps | Set.null ps = subfn v
+onAllValuations append subfn v ps =
+    case Set.minView ps of
+      Nothing -> error "onAllValuations"
+      Just (p, ps') ->
+          append -- Do the valuations of the remaining variables with  set to false
+                 (onAllValuations append subfn (Map.insert p False v) ps')
+                 -- Do the valuations of the remaining variables with  set to true
+                 (onAllValuations append subfn (Map.insert p True v) ps')
+
+type TruthTableRow = ([Bool], Bool)
+type TruthTable a = ([a], [TruthTableRow])
+
+truthTable :: forall formula atom. (PropositionalFormula formula atom, Eq atom, Ord atom) =>
+              formula -> TruthTable atom
+truthTable fm =
+    (atl, onAllValuations (++) mkRow Map.empty ats)
+    where
+      mkRow :: Map.Map atom Bool      -- ^ The current variable assignment
+            -> [TruthTableRow]          -- ^ The variable assignments and the formula value
+      mkRow v = [(map (\ k -> Map.findWithDefault False k v) atl, eval fm v)]
+      atl = Set.toAscList ats
+      ats = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- Recognizing tautologies.                                                  
+-- ------------------------------------------------------------------------- 
+
+tautology :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+tautology fm = onAllValuations (&&) (eval fm) Map.empty (atoms fm)
+
+-- ------------------------------------------------------------------------- 
+-- Related concepts.                                                         
+-- ------------------------------------------------------------------------- 
+
+
+unsatisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+unsatisfiable fm = tautology ((.~.) fm)
+
+satisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+satisfiable = not . unsatisfiable
+
+-- ------------------------------------------------------------------------- 
+-- Substitution operation.                                                   
+-- ------------------------------------------------------------------------- 
+
+-- pSubst :: Ord a => Map.Map a (Formula a) -> Formula a -> Formula a
+pSubst :: (PropositionalFormula formula atomic, Ord atomic) => Map.Map atomic formula -> formula -> formula
+pSubst subfn fm = on_atoms (\ p -> maybe (atomic p) id (fpf subfn p)) fm
+
+-- ------------------------------------------------------------------------- 
+-- Dualization.                                                              
+-- ------------------------------------------------------------------------- 
+
+dual :: forall formula atomic. (PropositionalFormula formula atomic) => formula -> formula
+dual fm =
+    foldPropositional co (fromBool . not) at fm
+    where
+      co ((:~:) _) = fm
+      co (BinOp p (:&:) q) = dual p .|. dual q
+      co (BinOp p (:|:) q) = dual p .&. dual q
+      co _ = error "dual: Formula involves connectives ==> or <=>";;
+      at = atomic
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+psimplify1 :: (PropositionalFormula r a, Eq r) => r -> r
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) fm') = foldPropositional simplifyNotCombine (fromBool . not) (\ _ -> fm) fm'
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _         ) -> r
+            (Just False, (:&:), _         ) -> false
+            (_,          (:&:), Just True ) -> l
+            (_,          (:&:), Just False) -> false
+            (Just True,  (:|:), _         ) -> true
+            (Just False, (:|:), _         ) -> r
+            (_,          (:|:), Just True ) -> true
+            (_,          (:|:), Just False) -> l
+            (Just True,  (:=>:), _         ) -> r
+            (Just False, (:=>:), _         ) -> true
+            (_,          (:=>:), Just True ) -> true
+            (_,          (:=>:), Just False) -> (.~.) l
+            (Just True,  (:<=>:), _         ) -> r
+            (Just False, (:<=>:), _         ) -> (.~.) r
+            (_,          (:<=>:), Just True ) -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _ -> fm
+
+      simplifyNotCombine ((:~:) p) = p
+      simplifyNotCombine _ = fm
+
+psimplify :: forall formula atomic. (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+psimplify fm =
+    foldPropositional c (\ _ -> fm) (\ _ -> fm) fm
+    where
+      c :: Combination formula -> formula
+      c ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      c (BinOp p op q) = psimplify1 (binop (psimplify p) op (psimplify q))
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.                                              
+-- ------------------------------------------------------------------------- 
+
+negative :: forall lit atom. Literal lit atom => lit -> Bool
+negative lit =
+    foldLiteral neg tf a lit
+    where
+      neg _ = True
+      tf = not
+      a _ = False
+
+positive :: Literal lit atom => lit -> Bool
+positive = not . negative
+
+negate :: PropositionalFormula formula atomic => formula -> formula
+negate lit =
+    foldPropositional c (fromBool . not) a lit
+    where
+      c ((:~:) p) = p
+      c _ = (.~.) lit
+      a _ = (.~.) lit
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf' :: PropositionalFormula formula atomic => formula -> formula
+nnf' fm =
+    foldPropositional nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfCombine ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf' ((.~.) p) .|. (nnf' q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf' p .&. nnf' q) .|. (nnf' ((.~.) p) .&. nnf' ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf' p .&. nnf' q
+      nnfCombine (BinOp p (:|:) q) = nnf' p .|. nnf' q
+      nnfNotCombine ((:~:) p) = nnf' p
+      nnfNotCombine (BinOp p (:&:) q) = nnf' ((.~.) p) .|. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf' ((.~.) p) .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf' p .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' ((.~.) q)) .|. nnf' ((.~.) p) .&. nnf' q
+
+-- ------------------------------------------------------------------------- 
+-- Roll in simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+nnf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nnf = nnf' . psimplify
+
+-- ------------------------------------------------------------------------- 
+-- Simple negation-pushing when we don't care to distinguish occurrences.    
+-- ------------------------------------------------------------------------- 
+
+nenf' :: PropositionalFormula formula atomic => formula -> formula
+nenf' fm =
+    foldPropositional nenfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nenfCombine ((:~:) p) = foldPropositional nenfNotCombine (\ _ -> fm) (\ _ -> fm) p
+      nenfCombine (BinOp p (:&:) q) = nenf' p .&. nenf' q
+      nenfCombine (BinOp p (:|:) q) = nenf' p .|. nenf' q
+      nenfCombine (BinOp p (:=>:) q) = nenf' ((.~.) p) .|. nenf' q
+      nenfCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' q
+      nenfNotCombine ((:~:) p) = p
+      nenfNotCombine (BinOp p (:&:) q) = nenf' ((.~.) p) .|. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:|:) q) = nenf' ((.~.) p) .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:=>:) q) = nenf' p .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' ((.~.) q) -- really?  how is this asymmetrical?
+
+nenf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nenf = nenf' . psimplify
+
+{-
+# Not (prime 2) ->
+  <<~(~(((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1))>>
+
+# nenf (Not (prime 2)) -> 
+  <<((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1>>
+
+> pretty ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N)))
+     (out0 ⇔ x0 ∧ y0) ∧ ¬out1 ∧ out1 ∧ ¬out0
+
+> pretty (nenf ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N))))
+     (out0 ⇔ x0 ∨ y0) ∨ ¬out1 ∨ out1 ∨ ¬out0
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Disjunctive normal form (DNF) via truth tables.                           
+-- ------------------------------------------------------------------------- 
+
+list_conj :: (PropositionalFormula formula atomic, Ord formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: PropositionalFormula formula atomic => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (PropositionalFormula formula atomic, Ord formula, Ord atomic) =>
+          Set.Set formula -> Map.Map atomic Bool -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+allSatValuations :: Ord a => (Map.Map a Bool -> Bool) -> Map.Map a Bool -> Set.Set a -> [Map.Map a Bool]
+allSatValuations subfn v pvs =
+    case Set.minView pvs of
+      Nothing -> if subfn v then [v] else []
+      Just (p, ps) -> (allSatValuations subfn (Map.insert p False v) ps) ++
+                      (allSatValuations subfn (Map.insert p True v) ps)
+
+dnf0 :: forall formula atomic. (PropositionalFormula formula atomic, Ord atomic, Ord formula) => formula -> formula
+dnf0 fm =
+    list_disj (Set.fromList (map (mkLits (Set.map atomic pvs)) satvals))
+    where
+      satvals = allSatValuations (eval fm) Map.empty pvs
+      pvs = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- DNF via distribution.                                                     
+-- ------------------------------------------------------------------------- 
+
+distrib :: PropositionalFormula formula atomic => formula -> formula
+distrib fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) s) =
+          foldPropositional c' tf a s
+          where c' (BinOp q (:|:) r) = distrib (p .&. q) .|. distrib (p .&. r)
+                c' _ =
+                    foldPropositional c'' tf a p
+                    where c'' (BinOp q (:|:) r) = distrib (q .&. s) .|. distrib (r .&. s)
+                          c'' _ = fm
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+rawdnf :: PropositionalFormula formula atomic => formula -> formula
+rawdnf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib (rawdnf p .&. rawdnf q)
+      c (BinOp p (:|:) q) = rawdnf p .|. rawdnf q
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.                                    
+-- ------------------------------------------------------------------------- 
+
+purednf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purednf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c ((:~:) p) = Set.map (Set.map (.~.)) (purednf p)
+      c _ = error "purednf" -- Set.singleton (Set.singleton fm)
+      tf x = Set.singleton (Set.singleton (fromBool x))
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Literal lit atom, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (pos, neg) = Set.partition positive lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpdnf fm =
+    foldPropositional c tf a fm
+    where
+      c :: Combination pf -> Set.Set (Set.Set lit)
+      c _ = Set.filter (\ d -> not (setAny (\ d' -> Set.isProperSubsetOf d' d) djs)) djs
+          where djs = Set.filter (not . trivial) (purednf (nnf fm))
+      tf = ifElse (Set.singleton Set.empty) Set.empty
+      a :: atom -> Set.Set (Set.Set lit)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Mapping back to a formula.                                                
+-- ------------------------------------------------------------------------- 
+
+dnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+dnf = list_disj . Set.map (list_conj . Set.map (toPropositional id))
+
+dnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+dnf' = dnf . (simpdnf :: pf -> Set.Set (Set.Set pf))
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+purecnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+simpcnf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpcnf fm =
+    foldPropositional c tf a fm
+    where
+      tf = ifElse Set.empty (Set.singleton Set.empty)
+      -- Discard any clause that is the proper subset of another clause
+      c _ = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+cnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+cnf = list_conj . Set.map (list_disj . Set.map (toPropositional id))
+
+cnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+cnf' = cnf . (simpcnf :: pf -> Set.Set (Set.Set pf))
diff --git a/testdata/logic/Data/Logic/Harrison/PropExamples.hs b/testdata/logic/Data/Logic/Harrison/PropExamples.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/PropExamples.hs
@@ -0,0 +1,392 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+module Data.Logic.Harrison.PropExamples
+    ( Atom(..)
+    , N
+    , prime
+    , ramsey
+    , tests
+    ) where
+
+import Data.Bits (Bits, shiftR)
+import Data.Logic.Classes.Combine ((.<=>.), (.=>.), (.&.), (.|.), Combinable, Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (true, false)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), botFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Lib (allsets)
+import Data.Logic.Harrison.Prop (tautology, list_conj, list_disj, psimplify)
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Set as Set
+import Prelude hiding (sum)
+import Test.HUnit
+import Text.PrettyPrint (text)
+
+tests :: Test
+tests = TestList [test01, test02, test03]
+
+-- ========================================================================= 
+-- Some propositional formulas to test, and functions to generate classes.   
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Generate assertion equivalent to R(s,t) <= n for the Ramsey number R(s,t) 
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P String a (Maybe a) deriving (Eq, Ord, Show)
+
+instance Pretty (Atom N) where
+    pretty (P s n mm) = text (s ++ show n ++ maybe "" (\ m -> "." ++ show m) mm)
+
+instance HasFixity (Atom N) where
+    fixity = const botFixity
+
+type N = Int
+
+ramsey :: forall formula.
+          (PropositionalFormula formula (Atom N), Ord formula) =>
+          Int -> Int -> N -> formula
+ramsey s t n =
+  let vertices = Set.fromList [1 .. n] in
+  let yesgrps = Set.map (allsets (2 :: Int)) (allsets s vertices)
+      nogrps = Set.map (allsets (2 :: Int)) (allsets t vertices) in
+  let e xs = let [m, n] = Set.toAscList xs in C.atomic (P "p" m (Just n)) in
+  list_disj (Set.map (list_conj . Set.map e) yesgrps) .|. list_disj (Set.map (list_conj . Set.map (\ p -> (.~.)(e p))) nogrps)
+
+-- ------------------------------------------------------------------------- 
+-- Some currently tractable examples.                                        
+-- ------------------------------------------------------------------------- 
+
+test01 :: Test
+test01 = TestList [{- TestCase (assertEqual "ramsey 3 3 4"
+                                             (Combine
+                                              (BinOp
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp
+                                                   (Atom (P "p" 1 (Just 4)))
+                                                   (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 2 (Just 4)))
+                                                     (:&:)
+                                                     (Atom (P "p" 1 (Just 2)))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 1 (Just 4)))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 3 (Just 4)))
+                                                       (:&:)
+                                                       (Atom (P "p" 1 (Just 3)))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 2 (Just 4)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 3 (Just 4)))
+                                                         (:&:)
+                                                         (Atom (P "p" 2 (Just 3)))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 1 (Just 3)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 2 (Just 3)))
+                                                         (:&:)
+                                                         (Atom (P "p" 1 (Just 2)))))))))))))
+                                               (:|:)
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 4))))) (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 1 (Just 2)))))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp (Combine
+                                                            ((:~:) (Atom (P "p" 1 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 2)))))))))))))))))
+                                         (ramsey 3 3 4 :: Formula (Atom N))), -}
+                   TestCase (assertEqual "tautology (ramsey 3 3 5)" False (tautology (ramsey 3 3 5 :: Formula (Atom N)))),
+                   TestCase (assertEqual "tautology (ramsey 3 3 6)" True (tautology (ramsey 3 3 6 :: Formula (Atom N))))]
+
+-- ------------------------------------------------------------------------- 
+-- Half adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+halfsum :: forall formula. Combinable formula => formula -> formula -> formula
+halfsum x y = x .<=>. ((.~.) y)
+
+halfcarry :: forall formula. Combinable formula => formula -> formula -> formula
+halfcarry x y = x .&. y
+
+ha :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula
+ha x y s c = (s .<=>. halfsum x y) .&. (c .<=>. halfcarry x y)
+
+-- ------------------------------------------------------------------------- 
+-- Full adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+carry :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+carry x y z = (x .&. y) .|. ((x .|. y) .&. z)
+
+sum :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+sum x y z = halfsum (halfsum x y) z
+
+fa :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula -> formula
+fa x y z s c = (s .<=>. sum x y z) .&. (c .<=>. carry x y z)
+
+-- ------------------------------------------------------------------------- 
+-- Useful idiom.                                                             
+-- ------------------------------------------------------------------------- 
+
+conjoin :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a) => (a -> formula) -> Set.Set a -> formula
+conjoin f l = list_conj (Set.map f l)
+
+-- ------------------------------------------------------------------------- 
+-- n-bit ripple carry adder with carry c(0) propagated in and c(n) out.      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> formula
+ripplecarry x y c out n =
+    conjoin (\ i -> fa (x i) (y i) (c i) (out i) (c(i + 1))) (Set.fromList [0 .. (n - 1)])
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+mk_index :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> formula
+mk_index x i = C.atomic (P x i Nothing)
+mk_index2 :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> a -> formula
+mk_index2 x i j = C.atomic (P x i (Just j))
+
+test02 = TestCase (assertEqual "ripplecarry x y c out 2"
+                               (Combine (BinOp (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 1 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 1 Nothing)))))))) (:&:)
+                                                         (Combine (BinOp (Atom (P "C" 2 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:&:) (Atom (P "Y" 1 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:|:) (Atom (P "Y" 1 Nothing)))) (:&:) (Atom (P "C" 1 Nothing)))))))))) (:&:)
+                                         (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 0 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 0 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 0 Nothing)))))))) (:&:)
+                                                   (Combine (BinOp (Atom (P "C" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:&:) (Atom (P "Y" 0 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:|:) (Atom (P "Y" 0 Nothing)))) (:&:) (Atom (P "C" 0 Nothing))))))))))))
+                               {- <<((OUT_0 <=> (X_0 <=> ~Y_0) <=> ~C_0) /\
+                                      (C_1 <=> X_0 /\ Y_0 \/ (X_0 \/ Y_0) /\ C_0)) /\
+                                     (OUT_1 <=> (X_1 <=> ~Y_1) <=> ~C_1) /\
+                                     (C_2 <=> X_1 /\ Y_1 \/ (X_1 \/ Y_1) /\ C_1)>> -}
+                               (let [x, y, out, c] = map mk_index ["X", "Y", "OUT", "C"] in
+                                ripplecarry x y c out 2 :: Formula (Atom N)))
+
+-- ------------------------------------------------------------------------- 
+-- Special case with 0 instead of c(0).                                      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry0 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry0 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then false else c i) out n)
+
+-- ------------------------------------------------------------------------- 
+-- Carry-select adder                                                        
+-- ------------------------------------------------------------------------- 
+
+ripplecarry1 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry1 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then true else c i) out n)
+
+mux :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+mux sel in0 in1 = (((.~.) sel) .&. in0) .|. (sel .&. in1)
+
+offset :: forall t a. Num a => a -> (a -> t) -> a -> t
+offset n x i = x (n + i)
+
+carryselect :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> a -> formula
+carryselect x y c0 c1 s0 s1 c s n k =
+  let k' = min n k in
+  let fm = ((ripplecarry0 x y c0 s0 k') .&. (ripplecarry1 x y c1 s1 k')) .&.
+           (((c k') .<=>. (mux (c 0) (c0 k') (c1 k'))) .&.
+            (conjoin (\ i -> (s i) .<=>. (mux (c 0) (s0 i) (s1 i)))
+                             (Set.fromList [0 .. (k' - 1)]))) in
+  if k' < k then fm else
+  fm .&. (carryselect
+          (offset k x) (offset k y) (offset k c0) (offset k c1)
+          (offset k s0) (offset k s1) (offset k c) (offset k s)
+          (n - k) k)
+
+-- ------------------------------------------------------------------------- 
+-- Equivalence problems for carry-select vs ripple carry adders.             
+-- ------------------------------------------------------------------------- 
+
+mk_adder_test :: forall formula a. (PropositionalFormula formula (Atom a), Ord a, Ord formula, Num a, Enum a) =>
+                 a -> a -> formula
+mk_adder_test n k =
+  let [x, y, c, s, c0, s0, c1, s1, c2, s2] =
+          map mk_index ["x", "y", "c", "s", "c0", "s0", "c1", "s1", "c2", "s2"] in
+  (((carryselect x y c0 c1 s0 s1 c s n k) .&.
+    ((.~.) (c 0))) .&.
+   (ripplecarry0 x y c2 s2 n)) .=>.
+  (((c n) .<=>. (c2 n)) .&.
+   (conjoin (\ i -> (s i) .<=>. (s2 i)) (Set.fromList [0 .. (n - 1)])))
+
+-- ------------------------------------------------------------------------- 
+-- Ripple carry stage that separates off the final result.                   
+--                                                                           
+--       UUUUUUUUUUUUUUUUUUUU  (u)                                           
+--    +  VVVVVVVVVVVVVVVVVVVV  (v)                                           
+--                                                                           
+--    = WWWWWWWWWWWWWWWWWWWW   (w)                                           
+--    +                     Z  (z)                                           
+-- ------------------------------------------------------------------------- 
+
+rippleshift :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> formula
+            -> (a -> formula)
+            -> a -> formula
+rippleshift u v c z w n =
+  ripplecarry0 u v (\ i -> if i == n then w(n - 1) else c(i + 1))
+                   (\ i -> if i == 0 then z else w(i - 1)) n
+-- ------------------------------------------------------------------------- 
+-- Naive multiplier based on repeated ripple carry.                          
+-- ------------------------------------------------------------------------- 
+
+multiplier :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+              (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> formula)
+           -> a
+           -> formula
+multiplier x u v out n =
+  if n == 1 then ((out 0) .<=>. (x 0 0)) .&. ((.~.)(out 1)) else
+  psimplify (((out 0) .<=>. (x 0 0)) .&.
+             ((rippleshift
+               (\ i -> if i == n - 1 then false else x 0 (i + 1))
+               (x 1) (v 2) (out 1) (u 2) n) .&.
+              (if n == 2 then ((out 2) .<=>. (u 2 0)) .&. ((out 3) .<=>. (u 2 1)) else
+                   conjoin (\ k -> rippleshift (u k) (x k) (v(k + 1)) (out k)
+                                   (if k == n - 1 then \ i -> out(n + i)
+                                    else u(k + 1)) n) (Set.fromList [2 .. (n - 1)]))))
+
+-- ------------------------------------------------------------------------- 
+-- Primality examples.                                                       
+-- For large examples, should use "num" instead of "int" in these functions. 
+-- ------------------------------------------------------------------------- 
+
+bitlength :: forall b a. (Num a, Num b, Bits b) => b -> a
+bitlength x = if x == 0 then 0 else 1 + bitlength (shiftR x 1);;
+
+bit :: forall a b. (Num a, Eq a, Bits b, Integral b) => a -> b -> Bool
+bit n x = if n == 0 then x `mod` 2 == 1 else bit (n - 1) (shiftR x 1)
+
+congruent_to :: forall formula atomic a b. (Bits b, PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Integral b, Enum a) =>
+                (a -> formula) -> b -> a -> formula
+congruent_to x m n =
+  conjoin (\ i -> if bit i m then x i else (.~.)(x i))
+          (Set.fromList [0 .. (n - 1)])
+
+prime :: forall formula. (PropositionalFormula formula (Atom N), Ord formula) => N -> formula
+prime p =
+  let [x, y, out] = map mk_index ["x", "y", "out"] in
+  let m i j = (x i) .&. (y j)
+      [u, v] = map mk_index2 ["u", "v"] in
+  let (n :: Int) = bitlength p in
+  (.~.) (multiplier m u v out (n - 1) .&. congruent_to out p (max n (2 * n - 2)))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+type F = Formula (Atom Int)
+
+deriving instance Show F
+
+{-
+instance Constants F where
+    fromBool True = 
+-}
+
+test03 :: Test
+test03 =
+    TestList [TestCase (assertEqual "tautology(prime 7)" True (tautology(prime 7 :: F))),
+              TestCase (assertEqual "tautology(prime 9)" False (tautology(prime 9 :: F))),
+              TestCase (assertEqual "tautology(prime 11)" True (tautology(prime 11 :: F)))]
diff --git a/testdata/logic/Data/Logic/Harrison/Resolution.hs b/testdata/logic/Data/Logic/Harrison/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Resolution.hs
@@ -0,0 +1,1045 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Resolution
+    ( resolution1
+    , resolution2
+    , resolution3
+    , presolution
+    , matchAtomsEq
+    ) where
+
+import Data.Logic.Classes.Atom (Atom(match))
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), zipFirstOrder)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(vt, foldTerm))
+import Data.Logic.Classes.Variable (Variable(prefix))
+import Data.Logic.Failing (Failing(..), failing)
+import Data.Logic.Harrison.FOL (subst, fv, generalize, list_disj, list_conj)
+import Data.Logic.Harrison.Lib (settryfind, allpairs, allsubsets, setAny, setAll,
+                                allnonemptysubsets, (|->), apply, defined)
+import Data.Logic.Harrison.Normal (simpdnf, simpcnf, trivial)
+import Data.Logic.Harrison.Skolem (pnf, SkolemT, askolemize, specialize)
+import Data.Logic.Harrison.Tableaux (unify_literals)
+import Data.Logic.Harrison.Unif (solve)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+
+-- ========================================================================= 
+-- Resolution.                                                               
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- MGU of a set of literals.                                                 
+-- ------------------------------------------------------------------------- 
+
+mgu :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+       Set.Set lit -> Map.Map v term -> Failing (Map.Map v term)
+mgu l env =
+    case Set.minView l of
+      Just (a, rest) ->
+          case Set.minView rest of
+            Just (b, _) -> unify_literals env a b >>= mgu rest
+            _ -> Success (solve env)
+      _ -> Success (solve env)
+
+unifiable :: (Literal lit atom, Term term v f, Atom atom term v) =>
+             lit -> lit -> Bool
+unifiable p q = failing (const False) (const True) (unify_literals Map.empty p q)
+
+-- ------------------------------------------------------------------------- 
+-- Rename a clause.                                                          
+-- ------------------------------------------------------------------------- 
+
+rename :: (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+          (v -> v) -> Set.Set fof -> Set.Set fof
+rename pfx cls =
+    Set.map (subst (Map.fromList (zip fvs vvs))) cls
+    where
+      -- fvs :: [v]
+      fvs = Set.toList (fv (list_disj cls))
+      -- vvs :: [term]
+      vvs = map (vt . pfx) fvs
+
+-- ------------------------------------------------------------------------- 
+-- General resolution rule, incorporating factoring as in Robinson's paper.  
+-- ------------------------------------------------------------------------- 
+
+resolvents :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+              Set.Set fof -> Set.Set fof -> fof -> Set.Set fof -> Set.Set fof
+resolvents cl1 cl2 p acc =
+    if Set.null ps2 then acc else Set.fold doPair acc pairs
+    where
+      doPair (s1,s2) sof =
+          case mgu (Set.union s1 (Set.map (.~.) s2)) Map.empty of
+            Success mp -> Set.union (Set.map (subst mp) (Set.union (Set.difference cl1 s1) (Set.difference cl2 s2))) sof
+            Failure _ -> sof
+      -- pairs :: Set.Set (Set.Set fof, Set.Set fof)
+      pairs = allpairs (,) (Set.map (Set.insert p) (allsubsets ps1)) (allnonemptysubsets ps2)
+      -- ps1 :: Set.Set fof
+      ps1 = Set.filter (\ q -> q /= p && unifiable p q) cl1
+      -- ps2 :: Set.Set fof
+      ps2 = Set.filter (unifiable ((.~.) p)) cl2
+
+resolve_clauses :: forall fof atom v term f.
+                   (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                   Set.Set fof -> Set.Set fof -> Set.Set fof
+resolve_clauses cls1 cls2 =
+    let cls1' = rename (prefix "x") cls1
+        cls2' = rename (prefix "y") cls2 in
+    Set.fold (resolvents cls1' cls2') Set.empty cls1'
+
+-- ------------------------------------------------------------------------- 
+-- Basic "Argonne" loop.                                                     
+-- ------------------------------------------------------------------------- 
+
+resloop1 :: forall atom v term f fof. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+resloop1 used unused =
+    maybe (Failure ["No proof found"]) step (Set.minView unused)
+    where
+      step (cl, ros) =
+          if Set.member Set.empty news then return True else resloop1 used' (Set.union ros news)
+          where
+            used' = Set.insert cl used
+            -- resolve_clauses is not in the Failing monad, so setmapfilter isn't appropriate.
+            news = Set.fold Set.insert Set.empty ({-setmapfilter-} Set.map (resolve_clauses cl) used')
+
+pure_resolution1 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution1 fm = resloop1 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution1 :: forall m fof term f atom v.
+               (Literal fof atom,
+                FirstOrderFormula fof atom v,
+                PropositionalFormula fof atom,
+                Term term v f,
+                Atom atom term v,
+                Ord fof,
+                Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution1 fm = askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution1 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Matching of terms and literals.                                           
+-- ------------------------------------------------------------------------- 
+
+term_match :: forall term v f. (Term term v f) => Map.Map v term -> [(term, term)] -> Failing (Map.Map v term)
+term_match env [] = Success env
+term_match env ((p, q) : oth) =
+    foldTerm v fn p
+    where
+      v x = if not (defined env x)
+            then term_match ((x |-> q) env) oth
+            else if apply env x == Just q
+                 then term_match env oth
+                 else Failure ["term_match"]
+      fn f fa =
+          foldTerm v' fn' q
+          where
+            fn' g ga | f == g && length fa == length ga = term_match env (zip fa ga ++ oth)
+            fn' _ _ = Failure ["term_match"]
+            v' _ = Failure ["term_match"]
+{-
+  case eqs of
+    [] -> Success env
+    (Fn f fa, Fn g ga) : oth
+        | f == g && length fa == length ga ->
+           term_match env (zip fa ga ++ oth)
+    (Var x, t) : oth ->
+        if not (defined env x) then term_match ((x |-> t) env) oth
+        else if apply env x == t then term_match env oth
+        else Failure ["term_match"]
+    _ -> Failure ["term_match"]
+-}
+
+match_literals :: forall term f v fof atom. (FirstOrderFormula fof atom v, Atom atom term v, Term term v f) =>
+                  Map.Map v term -> fof -> fof -> Failing (Map.Map v term)
+match_literals env t1 t2 =
+    fromMaybe err (zipFirstOrder qu co tf at t1 t2)
+    where
+      qu _ _ _ _ _ _ = Nothing
+      co ((:~:) p) ((:~:) q) = Just $ match_literals env p q
+      co _ _ = Nothing
+      tf a b = if a == b then Just (Success env) else Nothing
+      at a1 a2 = Just (match env a1 a2)
+      err = Failure ["match_literals"]
+
+-- Identical to unifyAtomsEq except calls term_match instead of unify.
+matchAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+matchAtomsEq env a1 a2 =
+    fromMaybe err (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p ts1 q ts2 =
+          if p == q && length ts1 == length ts2
+          then Just (term_match env (zip ts1 ts2))
+          else Nothing
+      tf p q = if p == q then Just (Success env) else Nothing
+      eq pl pr ql qr = Just (term_match env [(pl, ql), (pr, qr)])
+      err = Failure ["matchAtomsEq"]
+
+{-
+    case tmp of
+      (Atom (R p a1), Atom(R q a2)) -> term_match env [(Fn p a1, Fn q a2)]
+      (Not (Atom (R p a1)), Not (Atom (R q a2))) -> term_match env [(Fn p a1, Fn q a2)]
+      _ -> Failure ["match_literals"]
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Test for subsumption                                                      
+-- ------------------------------------------------------------------------- 
+
+subsumes_clause :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v) =>
+                   Set.Set fof -> Set.Set fof -> Bool
+subsumes_clause cls1 cls2 =
+    failing (const False) (const True) (subsume Map.empty cls1)
+    where
+      -- subsume :: Map.Map v term -> Set.Set fof -> Failing (Map.Map v term)
+      subsume env cls =
+          case Set.minView cls of
+            Nothing -> Success env
+            Just (l1, clt) -> settryfind (\ l2 -> case (match_literals env l1 l2) of
+                                                    Success env' -> subsume env' clt
+                                                    Failure msgs -> Failure msgs) cls2
+-- ------------------------------------------------------------------------- 
+-- With deletion of tautologies and bi-subsumption with "unused".            
+-- ------------------------------------------------------------------------- 
+
+replace :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set fof
+        -> Set.Set (Set.Set fof)
+        -> Set.Set (Set.Set fof)
+replace cl st =
+    case Set.minView st of
+      Nothing -> Set.singleton cl
+      Just (c, st') -> if subsumes_clause cl c
+                       then Set.insert cl st'
+                       else Set.insert c (replace cl st')
+
+incorporate :: forall fof term f v atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+               Set.Set fof
+            -> Set.Set fof
+            -> Set.Set (Set.Set fof)
+            -> Set.Set (Set.Set fof)
+incorporate gcl cl unused =
+    if trivial cl || setAny (\ c -> subsumes_clause c cl) (Set.insert gcl unused)
+    then unused
+    else replace cl unused
+
+resloop2 :: forall fof term f v atom. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof)
+         -> Set.Set (Set.Set fof)
+         -> Failing Bool
+resloop2 used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl {- :: Set.Set fof-}, ros {- :: Set.Set (Set.Set fof) -}) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = {-Set.fold Set.union Set.empty-} (Set.map (resolve_clauses cl) used') in
+          if Set.member Set.empty news then return True else resloop2 used' (Set.fold (incorporate cl) ros news)
+
+pure_resolution2 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution2 fm = resloop2 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution2 :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution2 fm = askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_resolution2 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Positive (P1) resolution.                                                 
+-- ------------------------------------------------------------------------- 
+
+presolve_clauses :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    Set.Set fof -> Set.Set fof -> Set.Set fof
+presolve_clauses cls1 cls2 =
+    if setAll positive cls1 || setAll positive cls2
+    then resolve_clauses cls1 cls2
+    else Set.empty
+
+presloop :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+presloop used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl, ros) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = Set.map (presolve_clauses cl) used' in
+          if Set.member Set.empty news
+          then Success True
+          else presloop used' (Set.fold (incorporate cl) ros news)
+
+pure_presolution :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_presolution fm = presloop Set.empty (simpcnf (specialize (pnf fm)))
+
+presolution :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+presolution fm =
+    askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_presolution . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Introduce a set-of-support restriction.                                   
+-- ------------------------------------------------------------------------- 
+
+pure_resolution3 :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution3 fm =
+    uncurry resloop2 (Set.partition (setAny positive) (simpcnf (specialize (pnf fm))))
+
+resolution3 :: forall fof atom term v f m. (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution3 fm =
+    askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution3 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+{-
+-- ------------------------------------------------------------------------- 
+-- The Pelletier examples again.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time presolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time presolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time presolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time presolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time presolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time presolution
+ <<p \/ ~p>>;;
+
+let p7 = time presolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time presolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time presolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time presolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time presolution
+ <<p <=> p>>;;
+
+let p12 = time presolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time presolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time presolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time presolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time presolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time presolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time presolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time presolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time presolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+let p21 = time presolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time presolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time presolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time presolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time presolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time presolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time presolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time presolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time presolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time presolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\
+   (forall x. (G(x) ==> ~U(x)) ==> P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time presolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time presolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time presolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time presolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time presolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time presolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time presolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+{- ** This one seems too slow
+
+let p38 = time presolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ** -}
+
+let p39 = time presolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time presolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time presolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+{- ** Also very slow
+
+let p42 = time presolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ** -}
+
+{- ** and this one too..
+
+let p43 = time presolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ** -}
+
+let p44 = time presolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+{- ** and this...
+
+let p45 = time presolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ** -}
+
+{- ** and this
+
+let p46 = time presolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time presolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time presolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time presolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time presolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time presolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+let gilmore_1 = time presolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time presolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time presolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time presolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time presolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time presolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This one still isn't easy!
+
+let gilmore_9 = time presolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time presolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+*********** -}
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Example                                                                   
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let gilmore_1 = resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletiers yet again.                                                     
+-- ------------------------------------------------------------------------- 
+
+{- ************
+
+let p1 = time resolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time resolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time resolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time resolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time resolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time resolution
+ <<p \/ ~p>>;;
+
+let p7 = time resolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time resolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time resolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time resolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time resolution
+ <<p <=> p>>;;
+
+let p12 = time resolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time resolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time resolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time resolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time resolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time resolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* Monadic Predicate Logic.                                                  *)
+(* ------------------------------------------------------------------------- *)
+
+let p18 = time resolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time resolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time resolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time resolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P) ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time resolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time resolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time resolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time resolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time resolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time resolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time resolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time resolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time resolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time resolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time resolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time resolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time resolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+   ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+  ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time resolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(*  Full predicate logic (without Identity and Functions)                    *)
+(* ------------------------------------------------------------------------- *)
+
+let p36 = time resolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time resolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+(*** This one seems too slow
+
+let p38 = time resolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ***)
+
+let p39 = time resolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time resolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time resolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+(*** Also very slow
+
+let p42 = time resolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ***)
+
+(*** and this one too..
+
+let p43 = time resolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ***)
+
+let p44 = time resolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+(*** and this...
+
+let p45 = time resolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ***)
+
+(*** and this
+
+let p46 = time resolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Manthey and Bry, CADE-9.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let p55 = time resolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time resolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* See info-hol, circa 1500.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let p58 = time resolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time resolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time resolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* From Gilmore's classic paper.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let gilmore_1 = time resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+(*** This is not valid, according to Gilmore
+
+let gilmore_2 = time resolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ***)
+
+let gilmore_3 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time resolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time resolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time resolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time resolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+(*** This one still isn't easy!
+
+let gilmore_9 = time resolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Davis-Putnam papers where Gilmore procedure is poor.         *)
+(* ------------------------------------------------------------------------- *)
+
+let davis_putnam_example = time resolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* The (in)famous Los problem.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let los = time resolution
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+************* -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/Skolem.hs b/testdata/logic/Data/Logic/Harrison/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Skolem.hs
@@ -0,0 +1,355 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Skolem
+    ( simplify
+    -- , simplify'
+    , lsimplify
+    , nnf
+    -- , nnf'
+    , pnf
+    -- , pnf'
+    , functions
+    -- , functions'
+    , SkolemT
+    , Skolem
+    , runSkolem
+    , runSkolemT
+    , specialize
+    , skolemize
+    -- , literal
+    , askolemize
+    , skolemNormalForm
+    -- , prenex'
+    , skolem
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (StateT(runStateT))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), true, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(exists, for_all, foldFirstOrder), Quant(..), quant, toPropositional)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import qualified Data.Logic.Classes.Skolem as C
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Classes.Variable (Variable(variant))
+import Data.Logic.Harrison.FOL (fv, subst)
+import Data.Logic.Harrison.Lib ((|=>))
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- =========================================================================
+-- Prenex and Skolem normal forms.                                           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification. Like "psimplify" but with quantifier clauses.     
+-- ------------------------------------------------------------------------- 
+
+simplify1 :: (FirstOrderFormula fof atom v,
+              -- Formula fof term v,
+              Atom atom term v,
+              Term term v f) => fof -> fof
+simplify1 fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = if Set.member x (fv p) then fm else p
+      co ((:~:) p) = foldFirstOrder (\ _ _ _ -> fm) nco (fromBool . not) (\ _ -> fm) p
+      co (BinOp l op r) = simplifyBinop l op r
+      nco ((:~:) p) = p
+      nco _ = fm
+      tf = fromBool
+      at _ = fm
+
+simplifyBinop :: forall p. (Constants p, Combinable p) => p -> BinOp -> p -> p
+simplifyBinop l op r =
+    case (asBool l, op, asBool r) of
+      (Just True,  (:&:), _         ) -> r
+      (Just False, (:&:), _         ) -> false
+      (_,          (:&:), Just True ) -> l
+      (_,          (:&:), Just False) -> false
+      (Just True,  (:|:), _         ) -> true
+      (Just False, (:|:), _         ) -> r
+      (_,          (:|:), Just True ) -> true
+      (_,          (:|:), Just False) -> l
+      (Just True,  (:=>:), _         ) -> r
+      (Just False, (:=>:), _         ) -> true
+      (_,          (:=>:), Just True ) -> true
+      (_,          (:=>:), Just False) -> (.~.) l
+      (Just False, (:<=>:), Just False) -> true
+      (Just True,  (:<=>:), _         ) -> r
+      (Just False, (:<=>:), _         ) -> (.~.) r
+      (_,          (:<=>:), Just True ) -> l
+      (_,          (:<=>:), Just False) -> (.~.) l
+      _ -> binop l op r
+
+simplify :: (FirstOrderFormula fof atom v,
+             Atom atom term v,
+             Term term v f) => fof -> fof
+simplify fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x fm' = simplify1 (quant op x (simplify fm'))
+      co ((:~:) fm') = simplify1 ((.~.) (simplify fm'))
+      co (BinOp fm1 op fm2) = simplify1 (binop (simplify fm1) op (simplify fm2))
+      tf = fromBool
+      at _ = fm
+
+-- | Just looks for double negatives and negated constants.
+lsimplify :: Literal lit atom => lit -> lit
+lsimplify fm = foldLiteral (lsimplify1 . (.~.) . lsimplify) fromBool (const fm) fm
+
+lsimplify1 :: Literal lit atom => lit -> lit
+lsimplify1 fm = foldLiteral (foldLiteral id (fromBool . not) (const fm)) fromBool (const fm) fm
+
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf :: FirstOrderFormula formula atom v => formula -> formula
+nnf fm =
+    foldFirstOrder nnfQuant nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfQuant op v p = quant op v (nnf p)
+      nnfCombine ((:~:) p) = foldFirstOrder nnfNotQuant nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf p .&. nnf q
+      nnfCombine (BinOp p (:|:) q) = nnf p .|. nnf q
+      nnfNotQuant Forall v p = exists v (nnf ((.~.) p))
+      nnfNotQuant Exists v p = for_all v (nnf ((.~.) p))
+      nnfNotCombine ((:~:) p) = nnf p
+      nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- ------------------------------------------------------------------------- 
+-- Prenex normal form.                                                       
+-- ------------------------------------------------------------------------- 
+
+pullQuants :: forall formula atom v term f. (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+              formula -> formula
+pullQuants fm =
+    foldFirstOrder (\ _ _ _ -> fm) pullQuantsCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      getQuant = foldFirstOrder (\ op v f -> Just (op, v, f)) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing)
+      pullQuantsCombine ((:~:) _) = fm
+      pullQuantsCombine (BinOp l op r) = 
+          case (getQuant l, op, getQuant r) of
+            (Just (Forall, vl, l'), (:&:), Just (Forall, vr, r')) -> pullq True  True  fm for_all (.&.) vl vr l' r'
+            (Just (Exists, vl, l'), (:|:), Just (Exists, vr, r')) -> pullq True  True  fm exists  (.|.) vl vr l' r'
+            (Just (Forall, vl, l'), (:&:), _)                     -> pullq True  False fm for_all (.&.) vl vl l' r
+            (_,                     (:&:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.&.) vr vr l  r'
+            (Just (Forall, vl, l'), (:|:), _)                     -> pullq True  False fm for_all (.|.) vl vl l' r
+            (_,                     (:|:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.|.) vr vr l  r'
+            (Just (Exists, vl, l'), (:&:), _)                     -> pullq True  False fm exists  (.&.) vl vl l' r
+            (_,                     (:&:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.&.) vr vr l  r'
+            (Just (Exists, vl, l'), (:|:), _)                     -> pullq True  False fm exists  (.|.) vl vl l' r
+            (_,                     (:|:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.|.) vr vr l  r'
+            _                                                     -> fm
+
+-- |Helper function to rename variables when we want to enclose a
+-- formula containing a free occurrence of that variable a quantifier
+-- that quantifies it.
+pullq :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) =>
+         Bool -> Bool
+      -> formula
+      -> (v -> formula -> formula)
+      -> (formula -> formula -> formula)
+      -> v -> v
+      -> formula -> formula
+      -> formula
+pullq l r fm mkq op x y p q =
+    let z = variant x (fv fm)
+        p' = if l then subst (x |=> vt z) p else p
+        q' = if r then subst (y |=> vt z) q else q
+        fm' = pullQuants (op p' q') in
+    mkq z fm'
+
+-- |Recursivly apply pullQuants anywhere a quantifier might not be
+-- leftmost.
+prenex :: (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+          formula -> formula 
+prenex fm =
+    foldFirstOrder qu co (\ _ -> fm) (\ _ -> fm) fm
+    where
+      qu op x p = quant op x (prenex p)
+      co (BinOp l (:&:) r) = pullQuants (prenex l .&. prenex r)
+      co (BinOp l (:|:) r) = pullQuants (prenex l .|. prenex r)
+      co _ = fm
+
+-- |Convert to Prenex normal form, with all quantifiers at the left.
+pnf :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) => formula -> formula
+pnf = prenex . nnf . simplify
+
+-- ------------------------------------------------------------------------- 
+-- Get the functions in a term and formula.                                  
+-- ------------------------------------------------------------------------- 
+
+-- FIXME: the function parameter should be a method in the Atom class,
+-- but we need to add a type parameter f to it first.
+functions :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- State monad for generating Skolem functions and constants.
+-- ------------------------------------------------------------------------- 
+
+-- | Harrison's code generated skolem functions by adding a prefix to
+-- the variable name they are based on.  Here we have a more general
+-- and type safe solution: we require that variables be instances of
+-- class Skolem which creates Skolem functions based on an integer.
+-- This state value exists in the SkolemT monad during skolemization
+-- and tracks the next available number and the current list of
+-- universally quantified variables.
+
+data SkolemState v term
+    = SkolemState
+      { skolemCount :: Int
+        -- ^ The next available Skolem number.
+      , univQuant :: [v]
+        -- ^ The variables which are universally quantified in the
+        -- current scope, in the order they were encountered.  During
+        -- Skolemization these are the parameters passed to the Skolem
+        -- function.
+      }
+
+-- | The state associated with the Skolem monad.
+newSkolemState :: SkolemState v term
+newSkolemState
+    = SkolemState
+      { skolemCount = 1
+      , univQuant = []
+      }
+
+-- | The Skolem monad transformer
+type SkolemT v term m = StateT (SkolemState v term) m
+
+-- | Run a computation in the Skolem monad.
+runSkolem :: SkolemT v term Identity a -> a
+runSkolem = runIdentity . runSkolemT
+
+-- | The Skolem monad
+type Skolem v term = StateT (SkolemState v term) Identity
+
+-- | Run a computation in a stacked invocation of the Skolem monad.
+runSkolemT :: Monad m => SkolemT v term m a -> m a
+runSkolemT action = (runStateT action) newSkolemState >>= return . fst
+
+-- ------------------------------------------------------------------------- 
+-- Core Skolemization function.                                              
+-- ------------------------------------------------------------------------- 
+
+-- |Skolemize the formula by removing the existential quantifiers and
+-- replacing the variables they quantify with skolem functions (and
+-- constants, which are functions of zero variables.)  The Skolem
+-- functions are new functions (obtained from the SkolemT monad) which
+-- are applied to the list of variables which are universally
+-- quantified in the context where the existential quantifier
+-- appeared.
+skolem :: (Monad m,
+           FirstOrderFormula fof atom v,
+           -- PropositionalFormula pf atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          fof -> SkolemT v term m fof
+skolem fm =
+    foldFirstOrder qu co (return . fromBool) (return . atomic) fm
+    where
+      -- We encountered an existentially quantified variable y,
+      -- allocate a new skolem function fx and do a substitution to
+      -- replace occurrences of y with fx.  The value of the Skolem
+      -- function is assumed to equal the value of y which satisfies
+      -- the formula.
+      qu Exists y p =
+          do let xs = fv fm
+             let fx = fApp (C.toSkolem y) (map vt (Set.toAscList xs))
+             skolem (subst (Map.singleton y fx) p)
+      qu Forall x p = skolem p >>= return . for_all x
+      co (BinOp l (:&:) r) = skolem2 (.&.) l r
+      co (BinOp l (:|:) r) = skolem2 (.|.) l r
+      co _ = return fm
+
+skolem2 :: (Monad m,
+            FirstOrderFormula fof atom v,
+            -- PropositionalFormula pf atom,
+            -- Formula formula term v,
+            Atom atom term v,
+            Term term v f) =>
+           (fof -> fof -> fof) -> fof -> fof -> SkolemT v term m fof
+skolem2 cons p q =
+    skolem p >>= \ p' ->
+    skolem q >>= \ q' ->
+    return (cons p' q')
+
+-- ------------------------------------------------------------------------- 
+-- Overall Skolemization function.                                           
+-- ------------------------------------------------------------------------- 
+
+-- |I need to consult the Harrison book for the reasons why we don't
+-- |just Skolemize the result of prenexNormalForm.
+askolemize :: forall m fof atom term v f.
+              (Monad m,
+               FirstOrderFormula fof atom v,
+               Atom atom term v,
+               Term term v f) =>
+              fof -> SkolemT v term m fof
+askolemize = skolem . nnf . simplify
+
+-- | Remove the leading universal quantifiers.  After a call to pnf
+-- this will be all the universal quantifiers, and the skolemization
+-- will have already turned all the existential quantifiers into
+-- skolem functions.
+specialize :: forall fof atom v. FirstOrderFormula fof atom v => fof -> fof
+specialize f =
+    foldFirstOrder q (\ _ -> f) (\ _ -> f) (\ _ -> f) f
+    where
+      q Forall _ f' = specialize f'
+      q _ _ _ = f
+
+-- | Skolemize and then specialize.  Because we know all quantifiers
+-- are gone we can convert to any instance of PropositionalFormula.
+skolemize :: forall m fof atom term v f pf atom2. (Monad m,
+              FirstOrderFormula fof atom v,
+              PropositionalFormula pf atom2,
+              Atom atom term v,
+              Term term v f,
+              Eq pf) =>
+             (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemize ca fm = askolemize fm >>= return . (toPropositional ca :: fof -> pf) . specialize . pnf
+
+{-
+-- | Convert a first order formula into a disjunct of conjuncts of
+-- literals.  Note that this can convert any instance of
+-- FirstOrderFormula into any instance of Literal.
+literal :: forall fof atom term v p f lit. (Literal fof atom, Apply atom p term, Term term v f, Literal lit atom, Formula lit atom, Ord lit) =>
+           fof -> Set.Set (Set.Set lit)
+literal fm =
+    foldLiteral neg tf at fm
+    where
+      neg :: fof -> Set.Set (Set.Set lit)
+      neg x = Set.map (Set.map (.~.)) (literal x)
+      tf = Set.singleton . Set.singleton . fromBool
+      at :: atom -> Set.Set (Set.Set lit)
+      at x = foldApply (\ _ _ -> Set.singleton (Set.singleton (atomic x))) tf x
+-}
+
+-- |We get Skolem Normal Form by skolemizing and then converting to
+-- Prenex Normal Form, and finally eliminating the remaining quantifiers.
+skolemNormalForm :: (FirstOrderFormula fof atom v,
+                     PropositionalFormula pf atom2,
+                     -- Formula fof term v,
+                     -- Formula pf term v,
+                     Atom atom term v,
+                     Term term v f,
+                     Monad m, Ord fof, Eq pf) =>
+                    (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemNormalForm = skolemize
diff --git a/testdata/logic/Data/Logic/Harrison/Tableaux.hs b/testdata/logic/Data/Logic/Harrison/Tableaux.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Tableaux.hs
@@ -0,0 +1,627 @@
+{-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Tableaux
+    ( unify_literals
+    , unifyAtomsEq
+    , deepen
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+--import Data.List (partition)
+import qualified Data.Logic.Classes.Atom as C
+--import Data.Logic.Classes.Combine ((.&.), (.=>.))
+--import Data.Logic.Classes.Constants (false)
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula, exists, for_all)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Negate (positive, (.~.))
+import Data.Logic.Classes.Literal (Literal, zipLiterals)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(..), vt)
+import Data.Logic.Harrison.FOL (subst, generalize)
+import Data.Logic.Harrison.Herbrand (davisputnam)
+import Data.Logic.Harrison.Lib (allpairs, settryfind, distrib')
+import Data.Logic.Harrison.Prop (simpdnf)
+import Data.Logic.Harrison.Skolem (runSkolem, skolemize)
+import Data.Logic.Harrison.Unif (unify)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Data.String (IsString(..))
+import Debug.Trace (trace)
+
+-- =========================================================================
+-- Tableaux, seen as an optimized version of a Prawitz-like procedure.       
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Unify literals (just pretend the toplevel relation is a function).        
+-- ------------------------------------------------------------------------- 
+
+unify_literals :: forall lit atom term v f.
+                  (Literal lit atom,
+                   C.Atom atom term v,
+                   Term term v f) =>
+                  Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_literals env f1 f2 =
+    maybe err id (zipLiterals co tf at f1 f2)
+    where
+      -- co :: lit -> lit -> Maybe (Failing (Map.Map v term))
+      co p q = Just $ unify_literals env p q
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      at :: atom -> atom -> Maybe (Failing (Map.Map v term))
+      at a1 a2 = Just $ C.unify env a1 a2
+      err = Failure ["Can't unify literals"]
+
+unifyAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+unifyAtomsEq env a1 a2 =
+    maybe err id (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p1 ts1 p2 ts2 =
+          if p1 == p2 && length ts1 == length ts2
+          then Just $ unify env (zip ts1 ts2)
+          else Nothing
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      eq pl pr ql qr = Just $ unify env [(pl, ql), (pr, qr)]
+      err = Failure ["Can't unify atoms"]
+
+-- ------------------------------------------------------------------------- 
+-- Unify complementary literals.                                             
+-- ------------------------------------------------------------------------- 
+
+unify_complements :: forall lit atom term v f.
+                     (Literal lit atom,
+                      C.Atom atom term v,
+                      Term term v f) =>
+                     Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_complements env p q = unify_literals env p ((.~.) q)
+
+-- ------------------------------------------------------------------------- 
+-- Unify and refute a set of disjuncts.                                      
+-- ------------------------------------------------------------------------- 
+
+unify_refute :: (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) => Set.Set (Set.Set lit) -> Map.Map v term -> Failing (Map.Map v term)
+unify_refute djs env =
+    case Set.minView djs of
+      Nothing -> Success env
+      Just (d, odjs) ->
+          settryfind (\ (p, n) -> unify_complements env p n >>= unify_refute odjs) pairs
+          where
+            pairs = allpairs (,) pos neg
+            (pos,neg) = Set.partition positive d
+
+-- ------------------------------------------------------------------------- 
+-- Hence a Prawitz-like procedure (using unification on DNF).                
+-- ------------------------------------------------------------------------- 
+
+prawitz_loop :: forall atom v term f lit. (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit) -> [v] -> Set.Set (Set.Set lit) -> Int -> (Map.Map v term, Int)
+prawitz_loop djs0 fvs djs n =
+    let l = length fvs in
+    let newvars = map (\ k -> fromString ("_" ++ show (n * l + k))) [1..l] in
+    let inst = Map.fromList (zip fvs (map vt newvars)) in
+    let djs1 = distrib' (Set.map (Set.map (mapAtoms (atomic . substitute' inst))) djs0) djs in
+    case unify_refute djs1 Map.empty of
+      Failure _ -> prawitz_loop djs0 fvs djs1 (n + 1)
+      Success env -> (env, n + 1)
+    where
+      substitute' :: Map.Map v term -> atom -> atom
+      substitute' = C.substitute
+
+-- prawitz :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Int
+prawitz :: forall fof atom term v f lit pf.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal lit atom,
+            Term term v f,
+            C.Atom atom term v) =>
+           fof -> Int
+prawitz fm =
+    snd (prawitz_loop dnf (Set.toList fvs) dnf0 0 :: (Map.Map v term, Int))
+    where
+      dnf0 = (Set.singleton Set.empty) :: Set.Set (Set.Set lit)
+      dnf = simpdnf pf :: Set.Set (Set.Set lit)
+      fvs = foldAtoms (\ s (a :: atom) -> Set.union (C.freeVariables a) s) Set.empty pf :: Set.Set v
+      pf = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+test01 = TestCase $ assertEqual "p20 - prawitz" expected input
+    where input = prawitz fm
+          fm = (for_all "x" (for_all "y" (exists "z" (for_all "w" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"] .=>.
+                                                                   pApp "R" [vt "z"] .&. pApp "U" [vt "w"]))))) .=>.
+               (exists "x" (exists "y" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"]))) .=>. (exists "z" (pApp "R" [vt "z"]))
+          expected = 1
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Comparison of number of ground instances.                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+compare :: forall fof pf lit atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            C.Atom atom term v,
+            IsString f) =>
+           (atom -> Set.Set (f, Int)) -> fof -> (Int, Failing Int)
+-}
+-- compare fa fm = (prawitz fm, davisputnam fa fm)
+{-
+START_INTERACTIVE;;
+test02 = TestCase $ assertEqual "p19" expected input
+    where input = compare (exists "x" (forall "y" (for_all "z" ((pApp "P" [vt "y"] .=>. pApp "Q" [vt "z"]) .=>. pApp "P" [vt "x"] .=>. pApp "Q" [vt "x"]))))
+
+let p20 = compare
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p24 = compare
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"])
+   .=>. (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p39 = compare
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p42 = compare
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+{- **** Too slow?
+
+let p43 = compare
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+ ***** -}
+
+let p44 = compare
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y)))
+   .=>. (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p59 = compare
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = compare
+ <<forall x. P(x,f[vt "x"]) .<=>.
+             exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- More standard tableau procedure, effectively doing DNF incrementally.     
+-- ------------------------------------------------------------------------- 
+
+let rec tableau (fms,lits,n) cont (env,k) =
+  if n < 0 then error "no proof at this level" else
+  match fms with
+    [] -> error "tableau: no proof"
+  | And(p,q) : unexp ->
+      tableau (p : q : unexp,lits,n) cont (env,k)
+  | Or(p,q) : unexp ->
+      tableau (p : unexp,lits,n) (tableau (q : unexp,lits,n) cont) (env,k)
+  | Forall(x,p) : unexp ->
+      let y = Vt("_" ++ string_of_int k) in
+      let p' = subst (x |=> y) p in
+      tableau (p' : unexp@[Forall(x,p)],lits,n-1) cont (env,k+1)
+  | fm : unexp ->
+      try tryfind (\ l -> cont(unify_complements env (fm,l),k)) lits
+      with Failure _ -> tableau (unexp,fm : lits,n) cont (env,k);;
+-}
+
+-- | Try f with higher and higher values of n until it succeeds, or
+-- optional maximum depth limit is exceeded.
+deepen :: (Int -> Failing t) -> Int -> Maybe Int -> Failing (t, Int)
+deepen _ n (Just m) | n > m = Failure ["Exceeded maximum depth limit"]
+deepen f n m =
+    -- If no maximum depth limit is given print a trace of the
+    -- levels tried.  The assumption is that we are running
+    -- interactively.
+    let n' = maybe (trace ("Searching with depth limit " ++ show n) n) (const n) m in
+    case f n' of
+      Failure _ -> deepen f (n + 1) m
+      Success x -> Success (x, n)
+
+{-
+let tabrefute fms =
+  deepen (\ n -> tableau (fms,[],n) (\ x -> x) (Map.empty,0); n) 0;;
+
+let tab fm =
+  let sfm = askolemize(Not(generalize fm)) in
+  if sfm = False then 0 else tabrefute [sfm];;
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p38 = tab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Try to split up the initial formula first; often a big improvement.       
+-- ------------------------------------------------------------------------- 
+
+let splittab fm = 
+  map tabrefute (simpdnf(askolemize(Not(generalize fm))));;
+
+-- ------------------------------------------------------------------------- 
+-- Example: the Andrews challenge.                                           
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p34 = splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Another nice example from EWD 1602.                                       
+-- ------------------------------------------------------------------------- 
+
+let ewd1062 = splittab
+ <<(forall x. x <= x) .&.
+   (forall x y z. x <= y .&. y <= z .=>. x <= z) .&.
+   (forall x y. f[vt "x"] <= y .<=>. x <= g[vt "y"])
+   .=>. (forall x y. x <= y .=>. f[vt "x"] <= f[vt "y"]) .&.
+       (forall x y. x <= y .=>. g[vt "x"] <= g[vt "y"])>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Do all the equality-free Pelletier problems, and more, as examples.       
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time splittab
+ <<p .=>. q .<=>. ~q .=>. ~p>>;;
+
+let p2 = time splittab
+ <<~ ~p .<=>. p>>;;
+
+let p3 = time splittab
+ <<~(p .=>. q) .=>. q .=>. p>>;;
+
+let p4 = time splittab
+ <<~p .=>. q .<=>. ~q .=>. p>>;;
+
+let p5 = time splittab
+ <<(p .|. q .=>. p .|. r) .=>. p .|. (q .=>. r)>>;;
+
+let p6 = time splittab
+ <<p .|. ~p>>;;
+
+let p7 = time splittab
+ <<p .|. ~ ~ ~p>>;;
+
+let p8 = time splittab
+ <<((p .=>. q) .=>. p) .=>. p>>;;
+
+let p9 = time splittab
+ <<(p .|. q) .&. (~p .|. q) .&. (p .|. ~q) .=>. ~(~q .|. ~q)>>;;
+
+let p10 = time splittab
+ <<(q .=>. r) .&. (r .=>. p .&. q) .&. (p .=>. q .&. r) .=>. (p .<=>. q)>>;;
+
+let p11 = time splittab
+ <<p .<=>. p>>;;
+
+let p12 = time splittab
+ <<((p .<=>. q) .<=>. r) .<=>. (p .<=>. (q .<=>. r))>>;;
+
+let p13 = time splittab
+ <<p .|. q .&. r .<=>. (p .|. q) .&. (p .|. r)>>;;
+
+let p14 = time splittab
+ <<(p .<=>. q) .<=>. (q .|. ~p) .&. (~q .|. p)>>;;
+
+let p15 = time splittab
+ <<p .=>. q .<=>. ~p .|. q>>;;
+
+let p16 = time splittab
+ <<(p .=>. q) .|. (q .=>. p)>>;;
+
+let p17 = time splittab
+ <<p .&. (q .=>. r) .=>. s .<=>. (~p .|. q .|. s) .&. (~p .|. ~r .|. s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletier problems: monadic predicate logic.                              
+-- ------------------------------------------------------------------------- 
+
+let p18 = time splittab
+ <<exists y. forall x. P[vt "y"] .=>. P[vt "x"]>>;;
+
+let p19 = time splittab
+ <<exists x. forall y z. (P[vt "y"] .=>. Q[vt "z"]) .=>. P[vt "x"] .=>. Q[vt "x"]>>;;
+
+let p20 = time splittab
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p21 = time splittab
+ <<(exists x. P .=>. Q[vt "x"]) .&. (exists x. Q[vt "x"] .=>. P)
+   .=>. (exists x. P .<=>. Q[vt "x"])>>;;
+
+let p22 = time splittab
+ <<(forall x. P .<=>. Q[vt "x"]) .=>. (P .<=>. (forall x. Q[vt "x"]))>>;;
+
+let p23 = time splittab
+ <<(forall x. P .|. Q[vt "x"]) .<=>. P .|. (forall x. Q[vt "x"])>>;;
+
+let p24 = time splittab
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"]) .=>.
+   (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p25 = time splittab
+ <<(exists x. P[vt "x"]) .&.
+   (forall x. U[vt "x"] .=>. ~G[vt "x"] .&. R[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. G[vt "x"] .&. U[vt "x"]) .&.
+   ((forall x. P[vt "x"] .=>. Q[vt "x"]) .|. (exists x. Q[vt "x"] .&. P[vt "x"]))
+   .=>. (exists x. Q[vt "x"] .&. P[vt "x"])>>;;
+
+let p26 = time splittab
+ <<((exists x. P[vt "x"]) .<=>. (exists x. Q[vt "x"])) .&.
+   (forall x y. P[vt "x"] .&. Q[vt "y"] .=>. (R[vt "x"] .<=>. U[vt "y"]))
+   .=>. ((forall x. P[vt "x"] .=>. R[vt "x"]) .<=>. (forall x. Q[vt "x"] .=>. U[vt "x"]))>>;;
+
+let p27 = time splittab
+ <<(exists x. P[vt "x"] .&. ~Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. R[vt "x"]) .&.
+   (forall x. U[vt "x"] .&. V[vt "x"] .=>. P[vt "x"]) .&.
+   (exists x. R[vt "x"] .&. ~Q[vt "x"])
+   .=>. (forall x. U[vt "x"] .=>. ~R[vt "x"])
+       .=>. (forall x. U[vt "x"] .=>. ~V[vt "x"])>>;;
+
+let p28 = time splittab
+ <<(forall x. P[vt "x"] .=>. (forall x. Q[vt "x"])) .&.
+   ((forall x. Q[vt "x"] .|. R[vt "x"]) .=>. (exists x. Q[vt "x"] .&. R[vt "x"])) .&.
+   ((exists x. R[vt "x"]) .=>. (forall x. L[vt "x"] .=>. M[vt "x"])) .=>.
+   (forall x. P[vt "x"] .&. L[vt "x"] .=>. M[vt "x"])>>;;
+
+let p29 = time splittab
+ <<(exists x. P[vt "x"]) .&. (exists x. G[vt "x"]) .=>.
+   ((forall x. P[vt "x"] .=>. H[vt "x"]) .&. (forall x. G[vt "x"] .=>. J[vt "x"]) .<=>.
+    (forall x y. P[vt "x"] .&. G[vt "y"] .=>. H[vt "x"] .&. J[vt "y"]))>>;;
+
+let p30 = time splittab
+ <<(forall x. P[vt "x"] .|. G[vt "x"] .=>. ~H[vt "x"]) .&.
+   (forall x. (G[vt "x"] .=>. ~U[vt "x"]) .=>. P[vt "x"] .&. H[vt "x"])
+   .=>. (forall x. U[vt "x"])>>;;
+
+let p31 = time splittab
+ <<~(exists x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"])) .&.
+   (exists x. Q[vt "x"] .&. P[vt "x"]) .&.
+   (forall x. ~H[vt "x"] .=>. J[vt "x"])
+   .=>. (exists x. Q[vt "x"] .&. J[vt "x"])>>;;
+
+let p32 = time splittab
+ <<(forall x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"]) .=>. Q[vt "x"]) .&.
+   (forall x. Q[vt "x"] .&. H[vt "x"] .=>. J[vt "x"]) .&.
+   (forall x. R[vt "x"] .=>. H[vt "x"])
+   .=>. (forall x. P[vt "x"] .&. R[vt "x"] .=>. J[vt "x"])>>;;
+
+let p33 = time splittab
+ <<(forall x. P[vt "a"] .&. (P[vt "x"] .=>. P[vt "b"]) .=>. P[vt "c"]) .<=>.
+   (forall x. P[vt "a"] .=>. P[vt "x"] .|. P[vt "c"]) .&. (P[vt "a"] .=>. P[vt "b"] .=>. P[vt "c"])>>;;
+
+let p34 = time splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+let p35 = time splittab
+ <<exists x y. P(x,y) .=>. (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Full predicate logic (without identity and functions).                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time splittab
+ <<(forall x. exists y. P(x,y)) .&.
+   (forall x. exists y. G(x,y)) .&.
+   (forall x y. P(x,y) .|. G(x,y)
+   .=>. (forall z. P(y,z) .|. G(y,z) .=>. H(x,z)))
+       .=>. (forall x. exists y. H(x,y))>>;;
+
+let p37 = time splittab
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) .=>. P(y,w)) .&. P(y,z) .&.
+     (P(y,w) .=>. (exists u. Q(u,w)))) .&.
+   (forall x z. ~P(x,z) .=>. (exists y. Q(y,z))) .&.
+   ((exists x y. Q(x,y)) .=>. (forall x. R(x,x))) .=>.
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time splittab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+
+let p39 = time splittab
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p40 = time splittab
+ <<(exists y. forall x. P(x,y) .<=>. P(x,x))
+  .=>. ~(forall x. exists y. forall z. P(z,y) .<=>. ~P(z,x))>>;;
+
+let p41 = time splittab
+ <<(forall z. exists y. forall x. P(x,y) .<=>. P(x,z) .&. ~P(x,x))
+  .=>. ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time splittab
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+let p43 = time splittab
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+let p44 = time splittab
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y))) .=>.
+   (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p45 = time splittab
+ <<(forall x.
+     P[vt "x"] .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y)) .=>.
+       (forall y. G[vt "y"] .&. H(x,y) .=>. R[vt "y"])) .&.
+   ~(exists y. L[vt "y"] .&. R[vt "y"]) .&.
+   (exists x. P[vt "x"] .&. (forall y. H(x,y) .=>.
+     L[vt "y"]) .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y))) .=>.
+   (exists x. P[vt "x"] .&. ~(exists y. G[vt "y"] .&. H(x,y)))>>;;
+
+let p46 = time splittab
+ <<(forall x. P[vt "x"] .&. (forall y. P[vt "y"] .&. H(y,x) .=>. G[vt "y"]) .=>. G[vt "x"]) .&.
+   ((exists x. P[vt "x"] .&. ~G[vt "x"]) .=>.
+    (exists x. P[vt "x"] .&. ~G[vt "x"] .&.
+               (forall y. P[vt "y"] .&. ~G[vt "y"] .=>. J(x,y)))) .&.
+   (forall x y. P[vt "x"] .&. P[vt "y"] .&. H(x,y) .=>. ~J(y,x)) .=>.
+   (forall x. P[vt "x"] .=>. G[vt "x"])>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Well-known "Agatha" example; cf. Manthey and Bry, CADE-9.                 
+-- ------------------------------------------------------------------------- 
+
+let p55 = time splittab
+ <<lives(agatha) .&. lives(butler) .&. lives(charles) .&.
+   (killed(agatha,agatha) .|. killed(butler,agatha) .|.
+    killed(charles,agatha)) .&.
+   (forall x y. killed(x,y) .=>. hates(x,y) .&. ~richer(x,y)) .&.
+   (forall x. hates(agatha,x) .=>. ~hates(charles,x)) .&.
+   (hates(agatha,agatha) .&. hates(agatha,charles)) .&.
+   (forall x. lives[vt "x"] .&. ~richer(x,agatha) .=>. hates(butler,x)) .&.
+   (forall x. hates(agatha,x) .=>. hates(butler,x)) .&.
+   (forall x. ~hates(x,agatha) .|. ~hates(x,butler) .|. ~hates(x,charles))
+   .=>. killed(agatha,agatha) .&.
+       ~killed(butler,agatha) .&.
+       ~killed(charles,agatha)>>;;
+
+let p57 = time splittab
+ <<P(f([vt "a"],b),f(b,c)) .&.
+   P(f(b,c),f(a,c)) .&.
+   (forall [vt "x"] y z. P(x,y) .&. P(y,z) .=>. P(x,z))
+   .=>. P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time splittab
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P[vt "x"] .&. Q[vt "y"]) .=>. ((P[vt "v"] .|. R[vt "w"])  .&. (R[vt "z"] .=>. Q[vt "v"])))>>;;
+
+let p59 = time splittab
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = time splittab
+ <<forall x. P(x,f[vt "x"]) .<=>.
+            exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **** This is still too hard for us! Amazing...
+
+let gilmore_1 = time splittab
+ <<exists x. forall y z.
+      ((F[vt "y"] .=>. G[vt "y"]) .<=>. F[vt "x"]) .&.
+      ((F[vt "y"] .=>. H[vt "y"]) .<=>. G[vt "x"]) .&.
+      (((F[vt "y"] .=>. G[vt "y"]) .=>. H[vt "y"]) .<=>. H[vt "x"])
+      .=>. F[vt "z"] .&. G[vt "z"] .&. H[vt "z"]>>;;
+
+ ***** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time splittab
+ <<exists x y. forall z.
+        (F(x,z) .<=>. F(z,y)) .&. (F(z,y) .<=>. F(z,z)) .&. (F(x,y) .<=>. F(y,x))
+        .=>. (F(x,y) .<=>. F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. H[vt "x"])) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. H[vt "z"]) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_4 = time splittab
+ <<exists x y. forall z.
+        (F(x,y) .=>. F(y,z) .&. F(z,z)) .&.
+        (F(x,y) .&. G(x,y) .=>. G(x,z) .&. G(z,z))>>;;
+
+let gilmore_5 = time splittab
+ <<(forall x. exists y. F(x,y) .|. F(y,x)) .&.
+   (forall x y. F(y,x) .=>. F(y,y))
+   .=>. exists z. F(z,z)>>;;
+
+let gilmore_6 = time splittab
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) .=>. G(v,u) .&. G(u,x))
+        .=>. (exists u. forall v. F(u,y) .=>. G(v,u) .&. G(u,y)) .|.
+            (forall u v. exists w. G(v,u) .|. H(w,y,u) .=>. G(u,w))>>;;
+
+let gilmore_7 = time splittab
+ <<(forall x. K[vt "x"] .=>. exists y. L[vt "y"] .&. (F(x,y) .=>. G(x,y))) .&.
+   (exists z. K[vt "z"] .&. forall u. L[vt "u"] .=>. F(z,u))
+   .=>. exists v w. K[vt "v"] .&. L[vt "w"] .&. G(v,w)>>;;
+
+let gilmore_8 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. (forall u. exists v. H(u,v,x)))) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. (forall u. exists v. H(u,v,z))) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_9 = time splittab
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))) .&.
+        ((forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))
+         .=>. ~(forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+         .=>. (forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x)) .&.
+             (forall u. exists v. F(z,u,v) .&. G(y,u) .&. ~H(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time splittab
+ <<exists x. exists y. forall z.
+        (F(x,y) .=>. (F(y,z) .&. F(z,z))) .&.
+        ((F(x,y) .&. G(x,y)) .=>. (G(x,z) .&. G(z,z)))>>;;
+
+************ -}
+
+-}
diff --git a/testdata/logic/Data/Logic/Harrison/Unif.hs b/testdata/logic/Data/Logic/Harrison/Unif.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Harrison/Unif.hs
@@ -0,0 +1,125 @@
+{-# OPTIONS -Wall #-}
+module Data.Logic.Harrison.Unif
+    ( unify
+    , solve
+    , fullUnify
+    , unifyAndApply
+    ) where
+
+import Data.Logic.Classes.Term (Term(..), tsubst)
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+{-
+(* ========================================================================= *)
+(* Unification for first order terms.                                        *)
+(*                                                                           *)
+(* Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  *)
+(* ========================================================================= *)
+
+let rec istriv env x t =
+  match t with
+    Var y -> y = x or defined env y & istriv env x (apply env y)
+  | Fn(f,args) -> exists (istriv env x) args & failwith "cyclic";;
+-}
+isTrivial :: Term term v f => Map.Map v term -> v -> term -> Failing Bool
+isTrivial env x t =
+    foldTerm v f t
+    where
+      v y =
+          if x == y
+          then Success True
+          else maybe (Success False) (isTrivial env x) (Map.lookup y env)
+      f _ args =
+          if any (failing (const False) id . isTrivial env x) args
+          then Failure ["cyclic"]
+          else Success False
+
+{-
+    foldT (\ y -> y == x || (defined env y && istriv env x (apply env y)))
+          (\ _ args -> if any (istriv env x) args then error "cyclic" else False)
+          t
+-}
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Main unification procedure                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec unify env eqs =
+  match eqs with
+    [] -> env
+  | (Fn(f,fargs),Fn(g,gargs))::oth ->
+        if f = g & length fargs = length gargs
+        then unify env (zip fargs gargs @ oth)
+        else failwith "impossible unification"
+  | (Var x,t)::oth | (t,Var x)::oth ->
+        if defined env x then unify env ((apply env x,t)::oth)
+        else unify (if istriv env x t then env else (x|->t) env) oth;;
+-}
+unify :: Term term v f => Map.Map v term -> [(term,term)] -> Failing (Map.Map v term)
+unify env [] = Success env
+unify env ((a,b):oth) =
+    foldTerm (vr b) (\ f fargs -> foldTerm (vr a) (fn f fargs) b) a
+    where
+      vr t x =
+          maybe (isTrivial env x t >>= \ trivial -> unify (if trivial then env else Map.insert x t env) oth)
+                (\ y -> unify env ((y, t) : oth))
+                (Map.lookup x env)
+      fn f fargs g gargs =
+          if f == g && length fargs == length gargs
+          then unify env (zip fargs gargs ++ oth)
+          else Failure ["impossible unification"]
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Solve to obtain a single instantiation.                                   *)
+(* ------------------------------------------------------------------------- *)
+
+let rec solve env =
+  let env' = mapf (tsubst env) env in
+  if env' = env then env else solve env';;
+-}
+solve :: Term term v f => Map.Map v term -> Map.Map v term
+solve env =
+    if env' == env then env else solve env'
+    where env' = Map.map (tsubst env) env
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Unification reaching a final solved form (often this isn't needed).       *)
+(* ------------------------------------------------------------------------- *)
+
+let fullunify eqs = solve (unify undefined eqs);;
+-}
+fullUnify :: Term term v f => [(term,term)] -> Failing (Map.Map v term)
+fullUnify eqs = failing Failure (Success . solve) (unify Map.empty eqs)
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Examples.                                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let unify_and_apply eqs =
+  let i = fullunify eqs in
+  let apply (t1,t2) = tsubst i t1,tsubst i t2 in
+  map apply eqs;;
+-}
+unifyAndApply :: Term term v f => [(term, term)] -> Failing [(term, term)]
+unifyAndApply eqs =
+    case fullUnify eqs of
+      Failure x -> Failure x
+      Success i -> Success (map (\ (t1, t2) -> (tsubst i t1, tsubst i t2)) eqs)
+{-
+
+START_INTERACTIVE;;
+unify_and_apply [<<|f(x,g(y))|>>,<<|f(f(z),w)|>>];;
+
+unify_and_apply [<<|f(x,y)|>>,<<|f(y,x)|>>];;
+
+(****  unify_and_apply [<<|f(x,g(y))|>>,<<|f(y,x)|>>];; *****)
+
+unify_and_apply [<<|x_0|>>,<<|f(x_1,x_1)|>>;
+                 <<|x_1|>>,<<|f(x_2,x_2)|>>;
+                 <<|x_2|>>,<<|f(x_3,x_3)|>>];;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/logic/Data/Logic/Instances/Chiou.hs b/testdata/logic/Data/Logic/Instances/Chiou.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Instances/Chiou.hs
@@ -0,0 +1,307 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             RankNTypes, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall -Wwarn -fno-warn-orphans -fno-warn-missing-signatures #-}
+module Data.Logic.Instances.Chiou
+    ( Sentence(..)
+    , CTerm(..)
+    , Connective(..)
+    , Quantifier(..)
+    , ConjunctiveNormalForm(..)
+    , NormalSentence(..)
+    , NormalTerm(..)
+    , toSentence
+    , fromSentence
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (Combinable(..), BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..), asBool, true, false)
+import Data.Logic.Classes.Equals (AtomEq(..), (.=.))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..), quant', pApp, prettyFirstOrder, fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..), (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..))
+import Data.Logic.Classes.Term (Term(..), Function)
+import Data.Logic.Classes.Variable (Variable)
+import qualified Data.Logic.Classes.FirstOrder as L
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.String (IsString(..))
+
+data Sentence v p f
+    = Connective (Sentence v p f) Connective (Sentence v p f)
+    | Quantifier Quantifier [v] (Sentence v p f)
+    | Not (Sentence v p f)
+    | Predicate p [CTerm v f]
+    | Equal (CTerm v f) (CTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+data CTerm v f
+    = Function f [CTerm v f]
+    | Variable v
+    deriving (Eq, Ord, Data, Typeable)
+
+data Connective
+    = Imply
+    | Equiv
+    | And
+    | Or
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+data Quantifier
+    = ForAll
+    | ExistsCh
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Negatable (Sentence v p f) where
+    negatePrivate = Not
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Constants p, Eq (Sentence v p f)) => Constants (Sentence v p f) where
+    fromBool x = Predicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance ({- Constants (Sentence v p f), -} Ord v, Ord p, Ord f) => Combinable (Sentence v p f) where
+    x .<=>. y = Connective x Equiv y
+    x .=>.  y = Connective x Imply y
+    x .|.   y = Connective x Or y
+    x .&.   y = Connective x And y
+
+instance (Predicate p, Function f v) => Formula (Sentence v p f) (Sentence v p f) where
+    atomic (Connective _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Quantifier _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Not _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic x@(Predicate _ _) = x
+    atomic x@(Equal _ _) = x
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f), Variable v, Predicate p, Function f v, Combinable (Sentence v p f)) =>
+         PropositionalFormula (Sentence v p f) (Sentence v p f) where
+    foldPropositional co tf at formula =
+        case formula of
+          Not x -> co ((:~:) x)
+          Quantifier _ _ _ -> error "Logic.Instance.Chiou.foldF0: unexpected"
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate p ts -> maybe (at (Predicate p ts)) tf (asBool p)
+          Equal t1 t2 -> at (Equal t1 t2)
+
+data AtomicFunction v
+    = AtomicFunction String
+    -- This is redundant with the SkolemFunction and SkolemConstant
+    -- constructors in the Chiou Term type.
+    | AtomicSkolemFunction v
+    deriving (Eq, Show)
+
+instance IsString (AtomicFunction v) where
+    fromString = AtomicFunction
+
+instance Variable v => Skolem (AtomicFunction v) v where
+    toSkolem = AtomicSkolemFunction
+    isSkolem (AtomicSkolemFunction _) = True
+    isSkolem _ = False
+
+-- The Atom type is not cleanly distinguished from the Sentence type, so we need an Atom instance for Sentence.
+instance (Variable v, Predicate p, Function f v) => Apply (Sentence v p f) p (CTerm v f) where
+    foldApply ap tf (Predicate p ts) = maybe (ap p ts) tf (asBool p)
+    foldApply _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    apply' = Predicate
+
+instance Predicate p => AtomEq (Sentence v p f) p (CTerm v f) where
+    foldAtomEq ap tf _ (Predicate p ts) = if p == true then tf True else if p == false then tf False else ap p ts
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    foldAtomEq _ _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    equals = Equal
+    applyEq' = Predicate
+
+instance (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Variable v, Predicate p, Function f v) => Pretty (Sentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Formula (Sentence v p f) (Sentence v p f), Predicate p, Function f v, Variable v) => HasFixity (Sentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f),
+          Variable v, Predicate p, Function f v) =>
+          FirstOrderFormula (Sentence v p f) (Sentence v p f) v where
+    for_all v x = Quantifier ForAll [v] x
+    exists v x = Quantifier ExistsCh [v] x
+    foldFirstOrder qu co tf at f =
+        case f of
+          Not x -> co ((:~:) x)
+          Quantifier op (v:vs) f' ->
+              let op' = case op of
+                          ForAll -> Forall
+                          ExistsCh -> Exists in
+              -- Use Logic.quant' here instead of the constructor
+              -- Quantifier so as not to create quantifications with
+              -- empty variable lists.
+              qu op' v (quant' op' vs f')
+          Quantifier _ [] f' -> foldFirstOrder qu co tf at f'
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate _ _ -> at f
+          Equal _ _ -> at f
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Not f1', Not f2') -> co ((:~:) f1') ((:~:) f2')
+          (Quantifier op1 (v1:vs1) f1', Quantifier op2 (v2:vs2) f2') ->
+              if op1 == op2
+              then let op' = case op1 of
+                               ForAll -> Forall
+                               ExistsCh -> Exists in
+                   qu op' v1 (Quantifier op1 vs1 f1') Forall v2 (Quantifier op2 vs2 f2')
+              else Nothing
+          (Quantifier q1 [] f1', Quantifier q2 [] f2') ->
+              if q1 == q2 then zipFirstOrder qu co tf at f1' f2' else Nothing
+          (Connective l1 op1 r1, Connective l2 op2 r2) ->
+              case (op1, op2) of
+                (And, And) -> co (BinOp l1 (:&:) r1) (BinOp l2 (:&:) r2)
+                (Or, Or) -> co (BinOp l1 (:|:) r1) (BinOp l2 (:|:) r2)
+                (Imply, Imply) -> co (BinOp l1 (:=>:) r1) (BinOp l2 (:=>:) r2)
+                (Equiv, Equiv) -> co (BinOp l1 (:<=>:) r1) (BinOp l2 (:<=>:) r2)
+                _ -> Nothing
+          (Equal _ _, Equal _ _) -> at f1 f2
+          (Predicate _ _, Predicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Variable v, Function f v) => Term (CTerm v f) v f where
+    foldTerm v fn t =
+        case t of
+          Variable x -> v x
+          Function f ts -> fn f ts
+    zipTerms  v f t1 t2 =
+        case (t1, t2) of
+          (Variable v1, Variable v2) -> v v1 v2
+          (Function f1 ts1, Function f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Variable
+    fApp f ts = Function f ts
+
+data ConjunctiveNormalForm v p f =
+    CNF [Sentence v p f]
+    deriving (Eq)
+
+data NormalSentence v p f
+    = NFNot (NormalSentence v p f)
+    | NFPredicate p [NormalTerm v f]
+    | NFEqual (NormalTerm v f) (NormalTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+-- We need a distinct type here because of the functional dependencies
+-- in class FirstOrderFormula.
+data NormalTerm v f
+    = NormalFunction f [NormalTerm v f]
+    | NormalVariable v
+    deriving (Eq, Ord, Data, Typeable)
+
+instance (Constants p, Eq (NormalSentence v p f)) => Constants (NormalSentence v p f) where
+    fromBool x = NFPredicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance Negatable (NormalSentence v p f) where
+    negatePrivate = NFNot
+    foldNegation normal inverted (NFNot x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+{-
+instance (Arity p, Constants p, Combinable (NormalSentence v p f)) => Pred p (NormalTerm v f) (NormalSentence v p f) where
+    pApp0 x = NFPredicate x []
+    pApp1 x a = NFPredicate x [a]
+    pApp2 x a b = NFPredicate x [a,b]
+    pApp3 x a b c = NFPredicate x [a,b,c]
+    pApp4 x a b c d = NFPredicate x [a,b,c,d]
+    pApp5 x a b c d e = NFPredicate x [a,b,c,d,e]
+    pApp6 x a b c d e f = NFPredicate x [a,b,c,d,e,f]
+    pApp7 x a b c d e f g = NFPredicate x [a,b,c,d,e,f,g]
+    x .=. y = NFEqual x y
+    x .!=. y = NFNot (NFEqual x y)
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Variable v, Predicate p, Function f v, Combinable (NormalSentence v p f)) => Pretty (NormalSentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Predicate p, Function f v, Combinable (NormalSentence v p f)) => Formula (NormalSentence v p f) (NormalSentence v p f) where
+    atomic x@(NFPredicate _ _) = x
+    atomic x@(NFEqual _ _) = x
+    atomic _ = error "Chiou: atomic"
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f), Combinable (NormalSentence v p f), Term (NormalTerm v f) v f,
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (NormalSentence v p f) (NormalSentence v p f) v where
+    for_all _ _ = error "FirstOrderFormula NormalSentence"
+    exists _ _ = error "FirstOrderFormula NormalSentence"
+    foldFirstOrder _ co tf at f =
+        case f of
+          NFNot x -> co ((:~:) x)
+          NFEqual _ _ -> at f
+          NFPredicate p _ -> maybe (at f) tf (asBool p)
+{-
+    zipFirstOrder _ co tf at f1 f2 =
+        case (f1, f2) of
+          (NFNot f1', NFNot f2') -> co ((:~:) f1') ((:~:) f2')
+          (NFEqual _ _, NFEqual _ _) -> at f1 f2
+          (NFPredicate _ _, NFPredicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Combinable (NormalSentence v p f), Predicate p, Function f v, Variable v) => HasFixity (NormalSentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Variable v, Function f v) => Term (NormalTerm v f) v f where
+    vt = NormalVariable
+    fApp = NormalFunction
+    foldTerm v f t =
+            case t of
+              NormalVariable x -> v x
+              NormalFunction x ts -> f x ts
+    zipTerms v fn t1 t2 =
+        case (t1, t2) of
+          (NormalVariable x1, NormalVariable x2) -> v x1 x2
+          (NormalFunction f1 ts1, NormalFunction f2 ts2) -> fn f1 ts1 f2 ts2
+          _ -> Nothing
+
+toSentence :: (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Atom (Sentence v p f) (CTerm v f) v, Function f v, Variable v, Predicate p) =>
+              NormalSentence v p f -> Sentence v p f
+toSentence (NFNot s) = (.~.) (toSentence s)
+toSentence (NFEqual t1 t2) = toTerm t1 .=. toTerm t2
+toSentence (NFPredicate p ts) = pApp p (map toTerm ts)
+
+toTerm :: (Variable v, Function f v) => NormalTerm v f -> CTerm v f
+toTerm (NormalFunction f ts) = fApp f (map toTerm ts)
+toTerm (NormalVariable v) = vt v
+
+fromSentence :: forall v p f. (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Predicate p) =>
+                Sentence v p f -> NormalSentence v p f
+fromSentence = foldFirstOrder 
+                 (\ _ _ _ -> error "fromSentence 1")
+                 (\ cm ->
+                      case cm of
+                        ((:~:) f) -> NFNot (fromSentence f)
+                        _ -> error "fromSentence 2")
+                 (\ x -> NFPredicate (fromBool x) [])
+                 (foldAtomEq (\ p ts -> NFPredicate p (map fromTerm ts))
+                             (\ x -> NFPredicate (fromBool x) [])
+                             (\ t1 t2 -> NFEqual (fromTerm t1) (fromTerm t2)))
+
+fromTerm :: CTerm v f -> NormalTerm v f
+fromTerm (Function f ts) = NormalFunction f (map fromTerm ts)
+fromTerm (Variable v) = NormalVariable v
diff --git a/testdata/logic/Data/Logic/Instances/PropLogic.hs b/testdata/logic/Data/Logic/Instances/PropLogic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Instances/PropLogic.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.PropLogic
+    ( flatten
+    , plSat0
+    , plSat
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..), clauseNormalForm', prettyPropositional, fixityPropositional, foldAtomsPropositional, mapAtomsPropositional)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set.Extra as S
+import PropLogic
+
+instance Negatable (PropForm a) where
+    negatePrivate = N
+    foldNegation normal inverted (N x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance {- Ord a => -} Combinable (PropForm a) where
+    x .<=>. y = EJ [x, y]
+    x .=>.  y = SJ [x, y]
+    x .|.   y = DJ [x, y]
+    x .&.   y = CJ [x, y]
+
+instance (Pretty a, HasFixity a, Ord a) => Formula (PropForm a) a where
+    atomic = A
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (PropForm a), Pretty a, HasFixity a, Ord a) => PropositionalFormula (PropForm a) a where
+    foldPropositional co tf at formula =
+        case formula of
+          -- EJ [x,y,z,...] -> CJ [EJ [x,y], EJ[y,z], ...]
+          EJ [] -> error "Empty equijunct"
+          EJ [x] -> foldPropositional co tf at x
+          EJ [x0, x1] -> co (BinOp x0 (:<=>:) x1)
+          EJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> EJ [x0, x1]) (pairs xs)))
+          SJ [] -> error "Empty subjunct"
+          SJ [x] -> foldPropositional co tf at x
+          SJ [x0, x1] -> co (BinOp x0 (:=>:) x1)
+          SJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> SJ [x0, x1]) (pairs xs)))
+          DJ [] -> tf False
+          DJ [x] -> foldPropositional co tf at x
+          DJ (x0:xs) -> co (BinOp x0 (:|:) (DJ xs))
+          CJ [] -> tf True
+          CJ [x] -> foldPropositional co tf at x
+          CJ (x0:xs) -> co (BinOp x0 (:&:) (CJ xs))
+          N x -> co ((:~:) x)
+          -- Not sure what to do about these - so far not an issue.
+          T -> tf True
+          F -> tf False
+          A x -> at x
+
+instance Constants (PropForm formula) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (PropositionalFormula (PropForm atom) atom, Pretty atom, HasFixity atom) => Pretty (PropForm atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (PropositionalFormula (PropForm atom) atom, HasFixity atom) => HasFixity (PropForm atom) where
+    fixity = fixityPropositional
+
+pairs :: [a] -> [(a, a)]
+pairs (x:y:zs) = (x,y) : pairs (y:zs)
+pairs _ = []
+
+flatten :: PropForm a -> PropForm a
+flatten (CJ xs) =
+    CJ (concatMap f (map flatten xs))
+    where
+      f (CJ ys) = ys
+      f x = [x]
+flatten (DJ xs) =
+    DJ (concatMap f (map flatten xs))
+    where
+      f (DJ ys) = ys
+      f x = [x]
+flatten (EJ xs) = EJ (map flatten xs)
+flatten (SJ xs) = SJ (map flatten xs)
+flatten (N x) = N (flatten x)
+flatten x = x
+
+plSat0 :: (PropAlg a (PropForm formula), PropositionalFormula formula atom, Ord formula) => PropForm formula -> Bool
+plSat0 f = satisfiable . (\ (x :: PropForm formula) -> x) . clauses0 $ f
+
+clauses0 :: (PropositionalFormula formula atom, Ord formula) => PropForm formula -> PropForm formula
+clauses0 f = CJ . map DJ . map S.toList . S.toList $ clauseNormalForm' f
+
+plSat :: forall m formula atom term v f. (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+                formula -> SkolemT v term m Bool
+plSat f = clauses f >>= (\ (x :: PropForm formula) -> return x) >>= return . satisfiable
+
+clauses :: forall m formula atom term v f.
+           (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+           formula -> SkolemT v term m (PropForm formula)
+clauses f =
+    do (cnf :: S.Set (S.Set formula)) <- clauseNormalForm f
+       return . CJ . map DJ . map (map A) . map S.toList . S.toList $ cnf
diff --git a/testdata/logic/Data/Logic/Instances/SatSolver.hs b/testdata/logic/Data/Logic/Instances/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Instances/SatSolver.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.SatSolver where
+
+import Control.Monad.State (get, put)
+import Control.Monad.Trans (lift)
+import Data.Boolean.SatSolver (Literal(Pos, Neg), CNF, newSatSolver, assertTrue', solve)
+import Data.Generics (Data, Typeable)
+import qualified Data.Set.Extra as S
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.ClauseNormalForm (ClauseNormalFormula(..))
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import qualified Data.Logic.Classes.Literal as N
+import Data.Logic.Classes.Negate (Negatable(..), negated, (.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import Data.Logic.Normal.Implicative (LiteralMapT, NormalT)
+import qualified Data.Map as M
+
+instance Ord Literal where
+    compare (Neg _) (Pos _) = LT
+    compare (Pos _) (Neg _) = GT
+    compare (Pos m) (Pos n) = compare m n
+    compare (Neg m) (Neg n) = compare m n
+
+instance Negatable Literal where
+    negatePrivate (Neg x) = Pos x
+    negatePrivate (Pos x) = Neg x
+    foldNegation _ inverted (Neg x) = inverted (Pos x)
+    foldNegation normal _ (Pos x) = normal (Pos x)
+
+deriving instance Data Literal
+deriving instance Typeable Literal
+
+instance ClauseNormalFormula CNF Literal where
+    clauses = S.fromList . map S.fromList
+    makeCNF = map S.toList . S.toList
+    satisfiable cnf = return . not . null $ assertTrue' cnf newSatSolver >>= solve
+
+toCNF :: (Monad m,
+          FirstOrderFormula formula atom v,
+          PropositionalFormula formula atom,
+          Atom atom term v,
+          AtomEq atom p term,
+          Term term v f,
+          N.Literal formula atom,
+          Ord formula) =>
+         formula -> NormalT formula v term m CNF
+toCNF f = clauseNormalForm f >>= S.ssMapM (lift . toLiteral) >>= return . makeCNF
+
+-- |Convert a [[formula]] to CNF, which means building a map from
+-- formula to Literal.
+toLiteral :: forall m lit. (Monad m, Negatable lit, Ord lit) =>
+             lit -> LiteralMapT lit m Literal
+toLiteral f =
+    literalNumber >>= return . if negated f then Neg else Pos
+    where
+      literalNumber :: LiteralMapT lit m Int
+      literalNumber =
+          get >>= \ (count, m) ->
+          case M.lookup f' m of
+            Nothing -> do let m' = M.insert f' count m
+                          put (count+1, m') 
+                          return count
+            Just n -> return n
+      f' :: lit
+      f' = if negated f then (.~.) f else f
diff --git a/testdata/logic/Data/Logic/KnowledgeBase.hs b/testdata/logic/Data/Logic/KnowledgeBase.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/KnowledgeBase.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, PackageImports,
+             RankNTypes, TemplateHaskell, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall #-}
+
+{- KnowledgeBase.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.KnowledgeBase
+    ( WithId(WithId, wiItem, wiIdent) -- Probably only used by some unit tests, and not really correctly
+    , ProverT
+    , runProver'
+    , runProverT'
+    , getKB
+    , unloadKB
+    -- , deleteKB
+    , askKB
+    , theoremKB
+    , inconsistantKB
+    , ProofResult(Proved, Disproved, Invalid)
+    , Proof(Proof, proofResult, proof)
+    , validKB
+    , tellKB
+    , loadKB
+    , showKB
+    ) where
+
+import "mtl" Control.Monad.Identity (Identity(runIdentity))
+import "mtl" Control.Monad.State (StateT, evalStateT, MonadState(get, put))
+import "mtl" Control.Monad.Trans (lift)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT, runSkolemT)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm)
+import Data.Logic.Resolution (prove, SetOfSupport, getSetOfSupport)
+import Data.SafeCopy (deriveSafeCopy, base)
+import qualified Data.Set.Extra as S
+import Prelude hiding (negate)
+
+type SentenceCount = Int
+
+data WithId a = WithId {wiItem :: a, wiIdent :: Int} deriving (Eq, Ord, Show, Data, Typeable)
+
+withId :: Int -> a -> WithId a
+withId i x = WithId {wiIdent = i, wiItem = x}
+
+{-
+withIdPairs :: [WithId a] -> [(a, Int)]
+withIdPairs = map (\ x -> (wiItem x, wiIdent x))
+
+wiLookupId :: Eq a => a -> [WithId a] -> Maybe Int
+wiLookupId x xs = lookup x (withIdPairs xs)
+
+withIdPairs' :: [WithId a] -> [(Int, a)]
+withIdPairs' = map (\ x -> (wiIdent x, wiItem x))
+
+wiLookupItem :: Int -> [WithId a] -> Maybe a
+wiLookupItem i xs = lookup i (withIdPairs' xs)
+-}
+
+type KnowledgeBase inf = S.Set (WithId inf)
+
+data ProverState inf
+    = ProverState
+      { recursionLimit :: Maybe Int
+      , knowledgeBase :: KnowledgeBase inf
+      , sentenceCount :: Int }
+
+zeroKB :: Maybe Int -> ProverState inf
+zeroKB limit =
+    ProverState
+         { recursionLimit = limit
+         , knowledgeBase = S.empty
+         , sentenceCount = 1 }
+
+-- |A monad for running the knowledge base.
+type ProverT inf = StateT (ProverState inf)
+type ProverT' v term inf m a = ProverT inf (SkolemT v term m) a
+
+runProverT' :: Monad m => Maybe Int -> ProverT' v term inf m a -> m a
+runProverT' limit = runSkolemT . runProverT limit
+runProverT :: Monad m => Maybe Int -> StateT (ProverState inf) m a -> m a
+runProverT limit action = evalStateT action (zeroKB limit)
+runProver' :: Maybe Int -> ProverT' v term inf Identity a -> a
+runProver' limit = runIdentity . runProverT' limit
+{-
+runProver :: StateT (ProverState inf) Identity a -> a
+runProver = runIdentity . runProverT
+-}
+
+data ProofResult
+    = Disproved
+    -- ^ The conjecture is unsatisfiable
+    | Proved
+    -- ^ The negated conjecture is unsatisfiable
+    | Invalid
+    -- ^ Both are satisfiable
+    deriving (Data, Typeable, Eq, Ord, Show)
+
+$(deriveSafeCopy 1 'base ''ProofResult)
+
+data Proof lit = Proof {proofResult :: ProofResult, proof :: S.Set (ImplicativeForm lit)} deriving (Data, Typeable, Eq, Ord)
+
+instance (Ord lit, Show lit, Literal lit atom, FirstOrderFormula lit atom v) => Show (Proof lit) where
+    show p = "Proof {proofResult = " ++ show (proofResult p) ++ ", proof = " ++ show (proof p) ++ "}"
+
+-- |Remove a particular sentence from the knowledge base
+unloadKB :: (Monad m, Ord inf) => SentenceCount -> ProverT inf m (Maybe (KnowledgeBase inf))
+unloadKB n =
+    do st <- get
+       let (discard, keep) = S.partition ((== n) . wiIdent) (knowledgeBase st)
+       put (st {knowledgeBase = keep}) >> return (Just discard)
+
+-- |Return the contents of the knowledgebase.
+getKB :: Monad m => ProverT inf m (S.Set (WithId inf))
+getKB = get >>= return . knowledgeBase
+
+-- |Return a flag indicating whether sentence was disproved, along
+-- with a disproof.
+inconsistantKB :: forall m formula atom term v p f lit.
+                  (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+                   Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+                  formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+inconsistantKB s =
+    get >>= \ st ->
+    lift (implicativeNormalForm s) >>=
+    return . getSetOfSupport >>= \ sos ->
+    getKB >>=
+    return . prove (recursionLimit st) S.empty sos . S.map wiItem
+
+-- |Return a flag indicating whether sentence was proved, along with a
+-- proof.
+theoremKB :: forall m formula atom term v p f lit.
+             (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+              Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+             formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+theoremKB s = inconsistantKB ((.~.) s)
+
+-- |Try to prove a sentence, return the result and the proof.
+-- askKB should be in KnowledgeBase module. However, since resolution
+-- is here functions are here, it is also placed in this module.
+askKB :: (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+          Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+         formula -> ProverT' v term (ImplicativeForm lit) m Bool
+askKB s = theoremKB s >>= return . fst
+
+-- |See whether the sentence is true, false or invalid.  Return proofs
+-- for truth and falsity.
+validKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+            Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+           formula -> ProverT' v term (ImplicativeForm lit) m (ProofResult, SetOfSupport lit v term, SetOfSupport lit v term)
+validKB s =
+    theoremKB s >>= \ (proved, proof1) ->
+    inconsistantKB s >>= \ (disproved, proof2) ->
+    return (if proved then Proved else if disproved then Disproved else Invalid, proof1, proof2)
+
+-- |Validate a sentence and insert it into the knowledgebase.  Returns
+-- the INF sentences derived from the new sentence, or Nothing if the
+-- new sentence is inconsistant with the current knowledgebase.
+tellKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+          formula -> ProverT' v term (ImplicativeForm lit) m (Proof lit)
+tellKB s =
+    do st <- get
+       inf <- lift (implicativeNormalForm s)
+       let inf' = S.map (withId (sentenceCount st)) inf
+       (valid, _, _) <- validKB s
+       case valid of
+         Disproved -> return ()
+         _ -> put st { knowledgeBase = S.union (knowledgeBase st) inf'
+                     , sentenceCount = sentenceCount st + 1 }
+       return $ Proof {proofResult = valid, proof = S.map wiItem inf'}
+
+loadKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+          [formula] -> ProverT' v term (ImplicativeForm lit) m [Proof lit]
+loadKB sentences = mapM tellKB sentences
+
+-- |Delete an entry from the KB.
+{-
+deleteKB :: Monad m => Int -> ProverT inf m String
+deleteKB i = do st <- get
+                modify (\ st' -> st' {knowledgeBase = deleteElement i (knowledgeBase st')})
+                st' <- get
+		return (if length (knowledgeBase st') /= length (knowledgeBase st) then
+			  "Deleted"
+			else
+			  "Failed to delete")
+	     
+deleteElement :: Int -> [a] -> [a]
+deleteElement i l
+    | i <= 0    = l
+    | otherwise = let
+		    (p1, p2) = splitAt (i - 1) l
+		  in
+		    p1 ++ (case p2 of
+			       [] -> []
+			       _ -> tail p2)
+-}
+
+-- |Return a text description of the contents of the knowledgebase.
+showKB :: (Show inf, Monad m) => ProverT inf m String
+showKB = get >>= return . reportKB
+
+reportKB :: (Show inf) => ProverState inf -> String
+reportKB st@(ProverState {knowledgeBase = kb}) =
+    case S.minView kb of
+      Nothing -> "Nothing in Knowledge Base\n"
+      Just (WithId {wiItem = x, wiIdent = n}, kb')
+          | S.null kb' ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n"
+          | True ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n" ++ reportKB (st {knowledgeBase = kb'})
diff --git a/testdata/logic/Data/Logic/Normal/Clause.hs b/testdata/logic/Data/Logic/Normal/Clause.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Normal/Clause.hs
@@ -0,0 +1,93 @@
+-- |A series of transformations to convert first order logic formulas
+-- into (ultimately) Clause Normal Form.
+-- 
+-- @
+-- 1st order formula:
+--   ∀Y (∀X (taller(Y,X) | wise(X)) => wise(Y))
+-- 
+-- Simplify
+--   ∀Y (~∀X (taller(Y,X) | wise(X)) | wise(Y))
+-- 
+-- Move negations in - Negation Normal Form
+--   ∀Y (∃X (~taller(Y,X) & ~wise(X)) | wise(Y))
+-- 
+-- Move quantifiers out - Prenex Normal Form
+--   ∀Y (∃X ((~taller(Y,X) & ~wise(X)) | wise(Y)))
+-- 
+-- Distribute disjunctions
+--   ∀Y ∃X ((~taller(Y,X) | wise(Y)) & (~wise(X) | wise(Y)))
+-- 
+-- Skolemize  - Skolem Normal Form
+--   ∀Y (~taller(Y,x(Y)) | wise(Y)) & (~wise(x(Y)) | wise(Y))
+-- 
+-- Convert to CNF
+--   { ~taller(Y,x(Y)) | wise(Y),
+--     ~wise(x(Y)) | wise(Y) } 
+-- @
+-- 
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Clause
+    ( clauseNormalForm
+    , cnfTrace
+    ) where
+
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq, prettyAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder)
+import Data.Logic.Classes.Literal (Literal(..), prettyLit)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Normal (simpcnf')
+import Data.Logic.Harrison.Skolem (skolemize, SkolemT, pnf, nnf, simplify)
+import qualified Data.Set.Extra as Set
+import Text.PrettyPrint (Doc, hcat, vcat, text, nest, ($$), brackets, render)
+
+-- |Convert to Skolem Normal Form and then distribute the disjunctions over the conjunctions:
+-- 
+-- @
+-- Formula      Rewrites to
+-- P | (Q & R)  (P | Q) & (P | R)
+-- (Q & R) | P  (Q | P) & (R | P)
+-- @
+-- 
+clauseNormalForm :: forall formula atom term v f lit m.
+                    (Monad m,
+                     FirstOrderFormula formula atom v,
+                     PropositionalFormula formula atom,
+                     Atom atom term v,
+                     Term term v f,
+                     Literal lit atom,
+                     Ord formula, Ord lit) =>
+                    formula -> SkolemT v term m (Set.Set (Set.Set lit))
+clauseNormalForm fm = skolemize id fm >>= return . (simpcnf' :: formula -> Set.Set (Set.Set lit))
+
+cnfTrace :: forall m formula atom term v p f lit.
+            (Monad m,
+             FirstOrderFormula formula atom v,
+             PropositionalFormula formula atom,
+             Atom atom term v,
+             AtomEq atom p term,
+             Term term v f,
+             Literal lit atom,
+             Ord formula, Ord lit) =>
+            (v -> Doc)
+         -> (p -> Doc)
+         -> (f -> Doc)
+         -> formula
+         -> SkolemT v term m (String, Set.Set (Set.Set lit))
+cnfTrace pv pp pf f =
+    do (snf :: formula) <- skolemize id f
+       cnf <- clauseNormalForm f
+       return (render (vcat
+                       [text "Original:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 f),
+                        text "Simplified:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (simplify f)),
+                        text "Negation Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (nnf . simplify $ f)),
+                        text "Prenex Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (pnf f)),
+                        text "Skolem Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 snf),
+                        text "Clause Normal Form:" $$ vcat (map prettyClause (fromSS cnf))]), cnf)
+    where
+      prettyClause (clause :: [lit]) =
+          nest 2 . brackets . hcat . intersperse (text ", ") . map (nest 2 . brackets . prettyLit (prettyAtomEq pv pp pf) pv 0) $ clause
+      fromSS = (map Set.toList) . Set.toList 
diff --git a/testdata/logic/Data/Logic/Normal/Implicative.hs b/testdata/logic/Data/Logic/Normal/Implicative.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Normal/Implicative.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE DeriveDataTypeable, PackageImports, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Implicative
+    ( LiteralMapT
+    , NormalT
+    , runNormal
+    , runNormalT
+    , ImplicativeForm(INF, neg, pos)
+    , makeINF'
+    , implicativeNormalForm
+    , prettyINF
+    , prettyProof
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (StateT(runStateT), MonadPlus, msum)
+import Data.Generics (Data, Typeable, listify)
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (true, ifElse)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Skolem (Skolem(isSkolem))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT, runSkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set.Extra as Set
+import qualified Data.Map as Map
+import Text.PrettyPrint (Doc, cat, text, hsep)
+
+-- |Combination of Normal monad and LiteralMap monad
+type NormalT formula v term m a = SkolemT v term (LiteralMapT formula m) a
+
+runNormalT :: Monad m => NormalT formula v term m a -> m a
+runNormalT action = runLiteralMapM (runSkolemT action)
+
+runNormal :: NormalT formula v term Identity a -> a
+runNormal = runIdentity . runNormalT
+ 
+--type LiteralMap f = LiteralMapT f Identity
+type LiteralMapT f = StateT (Int, Map.Map f Int)
+
+--runLiteralMap :: LiteralMap p a -> a
+--runLiteralMap action = runIdentity (runLiteralMapM action)
+
+runLiteralMapM :: Monad m => LiteralMapT f m a -> m a
+runLiteralMapM action = (runStateT action) (1, Map.empty) >>= return . fst
+
+-- |A type to represent a formula in Implicative Normal Form.  Such a
+-- formula has the form @a & b & c .=>. d | e | f@, where a thru f are
+-- literals.  One more restriction that is not implied by the type is
+-- that no literal can appear in both the pos set and the neg set.
+data ImplicativeForm lit =
+    INF {neg :: Set.Set lit, pos :: Set.Set lit}
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+-- |A version of MakeINF that takes lists instead of sets, used for
+-- implementing a more attractive show method.
+makeINF' :: (Negatable lit, Ord lit) => [lit] -> [lit] -> ImplicativeForm lit
+makeINF' n p = INF (Set.fromList n) (Set.fromList p)
+
+prettyINF :: (Negatable lit, Ord lit) => (lit -> Doc) -> ImplicativeForm lit -> Doc
+prettyINF lit x = cat $ [text "(", hsep (map lit (Set.toList (neg x))),
+                         text ") => (", hsep (map lit (Set.toList (pos x))), text ")"]
+
+prettyProof :: (Negatable lit, Ord lit) => (lit -> Doc) -> Set.Set (ImplicativeForm lit) -> Doc
+prettyProof lit p = cat $ [text "["] ++ intersperse (text ", ") (map (prettyINF lit) (Set.toList p)) ++ [text "]"]
+
+-- |Take the clause normal form, and turn it into implicative form,
+-- where each clauses becomes an (LHS, RHS) pair with the negated
+-- literals on the LHS and the non-negated literals on the RHS:
+-- @
+--   (a | ~b | c | ~d) becomes (b & d) => (a | c)
+--   (~b | ~d) | (a | c)
+--   ~~(~b | ~d) | (a | c)
+--   ~(b & d) | (a | c)
+-- @
+-- If there are skolem functions on the RHS, split the formula using
+-- this identity:
+-- @
+--   (a | b | c) => (d & e & f)
+-- @
+-- becomes
+-- @
+--    a | b | c => d
+--    a | b | c => e
+--    a | b | c => f
+-- @
+implicativeNormalForm :: forall m formula atom term v f lit. 
+                         (Monad m,
+                          FirstOrderFormula formula atom v,
+                          PropositionalFormula formula atom,
+                          Atom atom term v,
+                          Literal lit atom,
+                          Term term v f,
+                          Data formula, Ord formula, Ord lit, Data lit, Skolem f v) =>
+                         formula -> SkolemT v term m (Set.Set (ImplicativeForm lit))
+implicativeNormalForm formula =
+    do cnf <- clauseNormalForm formula
+       let pairs = Set.map (Set.fold collect (Set.empty, Set.empty)) cnf :: Set.Set (Set.Set lit, Set.Set lit)
+           pairs' = Set.flatten (Set.map split pairs) :: Set.Set (Set.Set lit, Set.Set lit)
+       return (Set.map (\ (n,p) -> INF n p) pairs')
+    where
+      collect :: lit -> (Set.Set lit, Set.Set lit) -> (Set.Set lit, Set.Set lit)
+      collect f (n, p) =
+          foldLiteral (\ f' -> (Set.insert f' n, p))
+                      (ifElse (n, Set.insert true p) (Set.insert true n, p))
+                      (\ _ -> (n, Set.insert f p))
+                      f
+      split :: (Set.Set lit, Set.Set lit) -> Set.Set (Set.Set lit, Set.Set lit)
+      split (lhs, rhs) =
+          if any isSkolem (gFind rhs :: [f])
+          then Set.map (\ x -> (lhs, Set.singleton x)) rhs
+          else Set.singleton (lhs, rhs)
+
+-- | @gFind a@ will extract any elements of type @b@ from
+-- @a@'s structure in accordance with the MonadPlus
+-- instance, e.g. Maybe Foo will return the first Foo
+-- found while [Foo] will return the list of Foos found.
+gFind :: (MonadPlus m, Data a, Typeable b) => a -> m b
+gFind = msum . map return . listify (const True)
diff --git a/testdata/logic/Data/Logic/Resolution.hs b/testdata/logic/Data/Logic/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Resolution.hs
@@ -0,0 +1,373 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS -Wall -Wwarn #-}
+
+{- Resolution.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.Resolution
+    ( prove
+    , getSetOfSupport
+    , SetOfSupport
+    , Unification
+    , isRenameOfAtomEq
+    , getSubstAtomEq
+    ) where
+
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Atom (Atom(isRename, getSubst))
+import Data.Logic.Classes.Constants (fromBool)
+import Data.Logic.Classes.Equals (AtomEq(foldAtomEq, equals), applyEq, zipAtomsEq)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal(..), zipLiterals)
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Normal.Implicative (ImplicativeForm(INF, neg, pos))
+import qualified Data.Set.Extra as S
+import Data.Map (Map, empty)
+import qualified Data.Map as Map
+import Data.Maybe (isJust)
+
+type SetOfSupport lit v term = S.Set (Unification lit v term)
+
+type Unification lit v term = (ImplicativeForm lit, Map.Map v term)
+
+prove :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, {-Show v, Show term,-} AtomEq atom p term, Predicate p) =>
+         Maybe Int -- ^ Recursion limit.  We continue recursing until this
+                   -- becomes zero.  If it is negative it may recurse until
+                   -- it overflows the stack.
+      -> SetOfSupport lit v term
+      -> SetOfSupport lit v term
+      -> S.Set (ImplicativeForm lit)
+      -> (Bool, SetOfSupport lit v term)
+prove (Just 0) ss1 _ _ = (False, ss1)
+prove limit ss1 ss2' kb  =
+    case S.minView ss2' of
+      Nothing -> (False, ss1)
+      Just (s, ss2) ->
+          case prove' s kb ss2 ss1 of
+            (ss', True) -> (True, (S.insert s (S.union ss1 ss')))
+            (ss', False) -> prove (maybe Nothing (\ n -> Just (n - 1)) limit) (S.insert s ss1) ss' (S.insert (fst s) kb)
+-- prove ss1 [] _kb = (False, ss1)
+-- prove ss1 (s:ss2) kb =
+--     let
+--       (ss', tf) = prove' s kb ss2 ss1
+--     in
+--       if tf then
+--         (True, (ss1 ++ [s] ++ss'))
+--       else
+--         prove (ss1 ++ [s]) ss' (fst s:kb)
+
+prove' :: forall lit atom p f v term.
+          (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+          Unification lit v term -> S.Set (ImplicativeForm lit) -> SetOfSupport lit v term -> SetOfSupport lit v term -> (SetOfSupport lit v term, Bool)
+prove' p kb ss1 ss2 =
+    let
+      res1 = S.map (\x -> resolution p (x, empty)) kb
+      res2 = S.map (\x -> resolution (x, empty) p) kb
+      dem1 = S.map (\e -> demodulate p (e, empty)) kb
+      dem2 = S.map (\p' -> demodulate (p', empty) p) kb
+      (ss', tf) = getResult (S.union ss1 ss2) (S.unions [res1, res2, dem1, dem2])
+    in
+      if S.null ss' then (ss1, False) else (S.union ss1 ss', tf)
+
+getResult :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+             SetOfSupport lit v term -> S.Set (Maybe (Unification lit v term)) -> ((SetOfSupport lit v term), Bool)
+getResult ss us =
+    case S.minView us of
+      Nothing ->
+          (S.empty, False)
+      Just (Nothing, xs) ->
+          getResult ss xs
+      Just ((Just x@(inf, _v)), xs) ->
+          if S.null (neg inf) && S.null (pos inf)
+          then (S.singleton x, True)
+          else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+               then getResult ss xs
+               else let (xs', tf) = getResult ss xs in (S.insert x xs', tf)
+{-
+getResult _ [] = (S.empty, False)
+getResult ss (Nothing:xs) = getResult ss xs
+getResult ss ((Just x):xs)  =
+    if S.null (neg inf) && S.null (pos inf)
+    then (S.singleton x, True)
+    else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+         then getResult ss xs
+         else let (xs', tf) = getResult ss xs in (S.insert x xs' tf)
+    where
+      (inf, _v) = x
+-}
+
+-- |Convert the "question" to a set of support.
+getSetOfSupport :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v) =>
+                   S.Set (ImplicativeForm lit) -> S.Set (ImplicativeForm lit, Map.Map v term)
+getSetOfSupport s = S.map (\ x -> (x, getSubsts x empty)) s
+
+getSubsts :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+             ImplicativeForm lit -> Map.Map v term -> Map.Map v term
+getSubsts inf theta =
+    getSubstSentences (pos inf) (getSubstSentences (neg inf) theta)
+
+getSubstSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> Map.Map v term -> Map.Map v term
+getSubstSentences xs theta = foldr getSubstSentence theta (S.toList xs)
+
+
+getSubstSentence :: (Literal lit atom, Atom atom term v, Term term v f)  => lit -> Map.Map v term -> Map.Map v term
+getSubstSentence formula theta =
+    foldLiteral
+          (\ s -> getSubstSentence s theta)
+          (const theta)
+          (getSubst theta)
+          formula
+
+getSubstAtomEq :: forall atom p term v f. (AtomEq atom p term, Term term v f) => Map v term -> atom -> Map v term
+getSubstAtomEq theta = foldAtomEq (\ _ ts -> getSubstsTerms ts theta) (const theta) (\ t1 t2 -> getSubstsTerms [t1, t2] theta)
+
+getSubstsTerms :: Term term v f => [term] -> Map.Map v term -> Map.Map v term
+getSubstsTerms [] theta = theta
+getSubstsTerms (x:xs) theta =
+    let
+      theta' = getSubstsTerm x theta
+      theta'' = getSubstsTerms xs theta'
+    in
+      theta''
+
+getSubstsTerm :: Term term v f => term -> Map.Map v term -> Map.Map v term
+getSubstsTerm term theta =
+    foldTerm (\ v -> Map.insertWith (\ _ old -> old) v (vt v) theta)
+             (\ _ ts -> getSubstsTerms ts theta)
+             term
+
+isRenameOf :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+              ImplicativeForm lit -> ImplicativeForm lit -> Bool
+isRenameOf inf1 inf2 =
+    (isRenameOfSentences lhs1 lhs2) && (isRenameOfSentences rhs1 rhs2)
+    where
+      lhs1 = neg inf1
+      rhs1 = pos inf1
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+isRenameOfSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> S.Set lit -> Bool
+isRenameOfSentences xs1 xs2 =
+    S.size xs1 == S.size xs2 && all (uncurry isRenameOfSentence) (zip (S.toList xs1) (S.toList xs2))
+
+isRenameOfSentence :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => lit -> lit -> Bool
+isRenameOfSentence f1 f2 =
+    maybe False id $
+    zipLiterals (\ _ _ -> Just False) (\ x y -> Just (x == y)) (\ x y -> Just (isRename x y)) f1 f2
+
+isRenameOfAtomEq :: (AtomEq atom p term, Term term v f) => atom -> atom -> Bool
+isRenameOfAtomEq a1 a2 =
+    maybe False id $
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> Just (p1 == p2 && isRenameOfTerms ts1 ts2))
+               (\ x y -> Just (x == y))
+               (\ t1l t1r t2l t2r -> Just (isRenameOfTerm t1l t2l && isRenameOfTerm t1r t2r))
+               a1 a2
+
+isRenameOfTerm :: Term term v f => term -> term -> Bool
+isRenameOfTerm t1 t2 =
+    maybe False id $
+    zipTerms (\ _ _ -> Just True)
+             (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && isRenameOfTerms ts1 ts2))
+             t1 t2
+
+isRenameOfTerms :: Term term v f => [term] -> [term] -> Bool
+isRenameOfTerms ts1 ts2 =
+    if length ts1 == length ts2 then
+      let
+        tsTuples = zip ts1 ts2
+      in
+        foldl (&&) True (map (\(t1, t2) -> isRenameOfTerm t1 t2) tsTuples)
+    else
+      False
+
+resolution :: forall lit atom p f term v. (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term, Eq p) =>
+             (ImplicativeForm lit, Map.Map v term) -> (ImplicativeForm lit, Map.Map v term) -> Maybe (ImplicativeForm lit, Map v term)
+resolution (inf1, theta1) (inf2, theta2) =
+    let
+        lhs1 = neg inf1
+        rhs1 = pos inf1
+        lhs2 = neg inf2
+        rhs2 = pos inf2
+        unifyResult = tryUnify rhs1 lhs2
+    in
+      case unifyResult of
+        Just ((rhs1', theta1'), (lhs2', theta2')) ->
+            let
+              lhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') lhs1)
+                              (S.catMaybes $ S.map (\s -> subst s theta2') lhs2')
+              rhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') rhs1')
+                              (S.catMaybes $ S.map (\s -> subst s theta2') rhs2)
+              theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2')
+            in
+              Just (INF lhs'' rhs'', theta)
+        Nothing -> Nothing
+    where
+      tryUnify :: (Literal lit atom, Ord lit) =>
+                  S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify lhs rhs = tryUnify' lhs rhs S.empty
+                         
+      tryUnify' :: (Literal lit atom, Ord lit) =>
+                   S.Set lit -> S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify' lhss _ _ | S.null lhss = Nothing
+      tryUnify' lhss'' rhss lhss' =
+          let (lhs, lhss) = S.deleteFindMin lhss'' in
+          case tryUnify'' lhs rhss S.empty of
+            Nothing -> tryUnify' lhss rhss (S.insert lhs lhss')
+            Just (rhss', theta1', theta2') ->
+                Just ((S.union lhss' lhss, theta1'), (rhss', theta2'))
+
+      tryUnify'' :: (Literal lit atom, Ord lit) =>
+                    lit -> S.Set lit -> S.Set lit -> Maybe (S.Set lit, Map.Map v term, Map.Map v term)
+      tryUnify'' _x rhss _ | S.null rhss = Nothing
+      tryUnify'' x rhss'' rhss' =
+          let (rhs, rhss) = S.deleteFindMin rhss'' in
+          case unify x rhs of
+            Nothing -> tryUnify'' x rhss (S.insert rhs rhss')
+            Just (theta1', theta2') -> Just (S.union rhss' rhss, theta1', theta2')
+
+-- |Try to unify the second argument using the equality in the first.
+demodulate :: (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term) =>
+              (Unification lit v term) -> (Unification lit v term) -> Maybe (Unification lit v term)
+demodulate (inf1, theta1) (inf2, theta2) =
+    case (S.null (neg inf1), S.toList (pos inf1)) of
+      (True, [lit1]) ->
+          foldLiteral (\ _ -> error "demodulate") (\ _ -> Nothing) (foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) p) lit1
+      _ -> Nothing
+    where
+      p t1 t2 =
+          case findUnify t1 t2 (S.union lhs2 rhs2) of
+            Just ((t1', t2'), theta1', theta2') ->
+                let substNeg2 = S.catMaybes $ S.map (\x -> subst x theta2') lhs2
+                    substPos2 = S.catMaybes $ S.map (\x -> subst x theta2') rhs2
+                    lhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substNeg2
+                    rhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substPos2
+                    theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2') in
+                Just (INF lhs rhs, theta)
+            Nothing -> Nothing
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+-- |Unification: unifies two sentences.
+unify :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+         lit -> lit -> Maybe (Map.Map v term, Map.Map v term)
+unify s1 s2 = unify' s1 s2 empty empty
+
+unify' :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+          lit -> lit -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unify' f1 f2 theta1 theta2 =
+    zipLiterals
+         (\ _ _ -> error "unify'")
+         (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+         (unify2AtomsEq theta1 theta2)
+         f1 f2
+
+unify2AtomsEq :: (AtomEq atom p term, Term term v f) => Map.Map v term -> Map.Map v term -> atom -> atom -> Maybe (Map.Map v term, Map.Map v term)
+unify2AtomsEq theta1 theta2 a1 a2 =
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> if p1 == p2 then unifyTerms ts1 ts2 theta1 theta2 else Nothing)
+               (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+               (\ l1 r1 l2 r2 -> unifyTerms [l1, r1] [l2, r2] theta1 theta2)
+               a1 a2
+
+unifyTerm :: Term term v f => term -> term -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerm t1 t2 theta1 theta2 =
+    foldTerm
+          (\ v1 ->
+               maybe (if vt v1 == t2 then Nothing else Just (Map.insert v1 t2 theta1, theta2))
+                     (\ t1' -> unifyTerm t1' t2 theta1 theta2)
+                     (Map.lookup v1 theta1))
+          (\ f1 ts1 ->
+               foldTerm (\ v2 -> maybe (Just (theta1, Map.insert v2 t1 theta2))
+                                 (\ t2' -> unifyTerm t1 t2' theta1 theta2)
+                                 (Map.lookup v2 theta2))
+                        (\ f2 ts2 -> if f1 == f2
+                                     then unifyTerms ts1 ts2 theta1 theta2
+                                     else Nothing)
+                        t2)
+          t1
+
+unifyTerms :: Term term v f =>
+              [term] -> [term] -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerms [] [] theta1 theta2 = Just (theta1, theta2)
+unifyTerms (t1:ts1) (t2:ts2) theta1 theta2 =
+    case (unifyTerm t1 t2 theta1 theta2) of
+      Nothing                -> Nothing
+      Just (theta1',theta2') -> unifyTerms ts1 ts2 theta1' theta2'
+unifyTerms _ _ _ _ = Nothing
+
+findUnify :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term) =>
+             term -> term -> S.Set lit -> Maybe ((term, term), Map.Map v term, Map.Map v term)
+findUnify tl tr s =
+    let
+      terms = concatMap (foldLiteral (\ (_ :: lit) -> error "getTerms") (\ _ -> []) p) (S.toList s)
+      unifiedTerms' = map (\t -> unifyTerm tl t empty empty) terms
+      unifiedTerms = filter isJust unifiedTerms'
+    in
+     case unifiedTerms of
+       [] -> Nothing
+       (Just (theta1, theta2)):_ ->
+         Just ((substTerm tl theta1, substTerm tr theta1), theta1, theta2)
+       (Nothing:_) -> error "findUnify"
+    where
+      -- getTerms lit = foldLiteral (\ _ -> error "getTerms") p formula
+      p :: atom -> [term]
+      p = foldAtomEq (\ _ ts -> concatMap getTerms' ts) (const []) (\ t1 t2 -> getTerms' t1 ++ getTerms' t2)
+      getTerms' :: term -> [term]
+      getTerms' t = foldTerm (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+
+{-
+getTerms :: Literal formula atom v => formula -> [term]
+getTerms formula =
+    foldLiteral (\ _ -> error "getTerms") p formula
+    where
+      getTerms' t = foldT (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+      p (Equal t1 t2) = getTerms' t1 ++ getTerms' t2
+      p (Apply _ ts) = concatMap getTerms' ts
+-}
+
+replaceTerm :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, Eq term, AtomEq atom p term) => lit -> (term, term) -> Maybe lit
+replaceTerm formula (tl', tr') =
+    foldLiteral
+          (\ _ -> error "error in replaceTerm")
+          (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (map (\ t -> replaceTerm' t) ts))))
+                      (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+                      (\ t1 t2 -> 
+                           let t1' = replaceTerm' t1
+                               t2' = replaceTerm' t2 in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+    where
+      replaceTerm' t =
+          if termEq t tl'
+          then tr'
+          else foldTerm vt (\ f ts -> fApp f (map replaceTerm' ts)) t
+      termEq t1 t2 =
+          maybe False id (zipTerms (\ v1 v2 -> Just (v1 == v2)) (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && length ts1 == length ts2 && all (uncurry termEq) (zip ts1 ts2))) t1 t2)
+
+subst :: (Literal formula atom, AtomEq atom p term, Atom atom term v, Term term v f, Eq term) => formula -> Map.Map v term -> Maybe formula
+subst formula theta =
+    foldLiteral
+          (\ _ -> Just formula)
+          (\ x -> Just (fromBool x))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (substTerms ts theta))))
+                      (Just . fromBool)
+                      (\ t1 t2 ->
+                           let t1' = substTerm t1 theta
+                               t2' = substTerm t2 theta in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+
+substTerm :: Term term v f => term -> Map.Map v term -> term
+substTerm term theta =
+    foldTerm (\ v -> maybe term id (Map.lookup v theta))
+             (\ f ts -> fApp f (substTerms ts theta))
+             term
+
+substTerms :: Term term v f => [term] -> Map.Map v term -> [term]
+substTerms ts theta = map (\t -> substTerm t theta) ts
+
+updateSubst :: Term term v f => Map.Map v term -> Map.Map v term -> Map.Map v term
+updateSubst theta1 theta2 = Map.union theta1 (Map.intersection theta1 theta2)
+-- This is what was in the original code, which behaves slightly differently
+--updateSubst theta1 _ | Map.null theta1 = Map.empty
+--updateSubst theta1 theta2 = Map.unionWith (\ _ term2 -> term2) theta1 theta2
diff --git a/testdata/logic/Data/Logic/Satisfiable.hs b/testdata/logic/Data/Logic/Satisfiable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Satisfiable.hs
@@ -0,0 +1,53 @@
+-- |Do satisfiability computations on any FirstOrderFormula formula by
+-- converting it to a convenient instance of PropositionalFormula and
+-- using the satisfiable function from that instance.  Currently we
+-- use the satisfiable function from the PropLogic package, by the
+-- Bucephalus project.
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Satisfiable
+    ( satisfiable
+    , theorem
+    , inconsistant
+    , invalid
+    ) where
+
+import qualified Data.Set as Set
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), toPropositional)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Instances.PropLogic ()
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified PropLogic as PL
+
+-- |Is there any variable assignment that makes the formula true?
+-- satisfiable :: forall formula atom term v f m. (Monad m, FirstOrderFormula formula atom v, Formula atom term v, Term term v f, Ord formula, Literal formula atom v, Ord atom) =>
+--                 formula -> SkolemT v term m Bool
+satisfiable :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                Ord atom, Monad m, Eq formula, Ord formula) =>
+               formula -> SkolemT v term m Bool
+satisfiable f =
+    do (clauses :: Set.Set (Set.Set formula)) <- clauseNormalForm f
+       let f' = PL.CJ . map (PL.DJ . map (toPropositional PL.A)) . map Set.toList . Set.toList $ clauses
+       return . PL.satisfiable $ f'
+
+-- |Is the formula always false?  (Not satisfiable.)
+inconsistant :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                 Ord atom, Monad m, Eq formula, Ord formula) =>
+                formula -> SkolemT v term m Bool
+inconsistant f =  satisfiable f >>= return . not
+
+-- |Is the negation of the formula inconsistant?
+theorem :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+theorem f = inconsistant ((.~.) f)
+
+-- |A formula is invalid if it is neither a theorem nor inconsistent.
+invalid :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+invalid f = inconsistant f >>= \ fi -> theorem f >>= \ ft -> return (not (fi || ft))
diff --git a/testdata/logic/Data/Logic/Types/Common.hs b/testdata/logic/Data/Logic/Types/Common.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Common.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Types.Common where
+
+import Data.Logic.Classes.Variable (Variable(..))
+import qualified Data.Set as Set
+import Text.PrettyPrint (text)
+
+instance Variable String where
+    variant x vars = if Set.member x vars then variant (x ++ "'") vars else x
+    prefix p x = p ++ x
+    prettyVariable = text
+
+{-
+instance Variable String where
+    variant v vs =
+        if Set.member v vs then variant (next v) (Set.insert v vs) else v
+        where
+          next :: String -> String
+          next s =
+              case break (not . isDigit) (reverse s) of
+                (_, "") -> "x"
+                ("", nondigits) -> nondigits ++ "2"
+                (digits, nondigits) -> nondigits ++ show (1 + read (reverse digits) :: Int)
+-}
diff --git a/testdata/logic/Data/Logic/Types/FirstOrder.hs b/testdata/logic/Data/Logic/Types/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/FirstOrder.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             GeneralizedNewtypeDeriving, MultiParamTypeClasses, TemplateHaskell, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures -fno-warn-orphans #-}
+-- |Data types which are instances of the Logic type class for use
+-- when you just want to use the classes and you don't have a
+-- particular representation you need to use.
+module Data.Logic.Types.FirstOrder
+    ( Formula(..)
+    , PTerm(..)
+    , Predicate(..)
+    ) where
+
+import Data.Data (Data)
+import qualified Data.Logic.Classes.Apply as C
+import qualified Data.Logic.Classes.Atom as C
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(..), asBool)
+import Data.Logic.Classes.Equals (AtomEq(..), (.=.), pApp, substAtomEq, varAtomEq, prettyAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..), prettyFirstOrder, fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), botFixity)
+import Data.Logic.Classes.Term (Term(..), Function)
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (isRenameOfAtomEq, getSubstAtomEq)
+import Data.SafeCopy (SafeCopy, base, deriveSafeCopy, extension, MigrateFrom(..))
+import Data.Typeable (Typeable)
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula v p f
+    = Predicate (Predicate p (PTerm v f))
+    | Combine (Combination (Formula v p f))
+    | Quant Quant v (Formula v p f)
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- |A temporary type used in the fold method to represent the
+-- combination of a predicate and its arguments.  This reduces the
+-- number of arguments to foldFirstOrder and makes it easier to manage the
+-- mapping of the different instances to the class methods.
+data Predicate p term
+    = Equal term term
+    | Apply p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | The range of a term is an element of a set.
+data PTerm v f
+    = Var v                         -- ^ A variable, either free or
+                                    -- bound by an enclosing quantifier.
+    | FunApp f [PTerm v f]           -- ^ Function application.
+                                    -- Constants are encoded as
+                                    -- nullary functions.  The result
+                                    -- is another term.
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+instance Negatable (Formula v p f) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants p => Constants (Formula v p f) where
+    fromBool = Predicate . fromBool
+    asBool (Predicate x) = asBool x
+    asBool _ = Nothing
+
+instance Constants p => Constants (Predicate p (PTerm v f)) where
+    fromBool x = Apply (fromBool x) []
+    asBool (Apply p _) = asBool p
+    asBool _ = Nothing
+
+instance (Constants (Formula v p f) {-, Ord v, Ord p, Ord f-}) => Combinable (Formula v p f) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+instance (C.Predicate p, Function f v) => C.Formula (Formula v p f) (Predicate p (PTerm v f)) where
+    atomic = Predicate
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v, Constants (Formula v p f), Combinable (Formula v p f)
+         ) => PropositionalFormula (Formula v p f) (Predicate p (PTerm v f)) where
+    foldPropositional co tf at formula =
+        maybe testFm tf (asBool formula)
+        where
+          testFm =
+              case formula of
+                Quant _ _ _ -> error "foldF0: quantifiers should not be present"
+                Combine x -> co x
+                Predicate x -> at x
+
+instance (Variable v, Function f v) => Term (PTerm v f) v f where
+    foldTerm vf fn t =
+        case t of
+          Var v -> vf v
+          FunApp f ts -> fn f ts
+    zipTerms v f t1 t2 =
+        case (t1, t2) of
+          (Var v1, Var v2) -> v v1 v2
+          (FunApp f1 ts1, FunApp f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Var
+    fApp x args = FunApp x args
+
+{-
+instance (Arity p, Constants p) => Atom (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtom ap (Apply p ts) = ap p ts
+    foldAtom ap (Constant x) = ap (fromBool x) []
+    foldAtom _ _ = error "foldAtom Predicate"
+    zipAtoms ap (Apply p1 ts1) (Apply p2 ts2) = ap p1 ts1 p2 ts2
+    zipAtoms ap (Constant x) (Constant y) = ap (fromBool x) [] (fromBool y) []
+    zipAtoms _ _ _ = error "zipAtoms Predicate"
+    apply' = Apply
+-}
+
+instance C.Predicate p => AtomEq (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtomEq ap tf _ (Apply p ts) = maybe (ap p ts) tf (asBool p)
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    equals = Equal
+    applyEq' = Apply
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          AtomEq (Predicate p (PTerm v f)) p (PTerm v f),
+          Constants (Formula v p f),
+          Variable v, C.Predicate p, Function f v
+         ) => FirstOrderFormula (Formula v p f) (Predicate p (PTerm v f)) v where
+    for_all v x = Quant Forall v x
+    exists v x = Quant Exists v x
+    foldFirstOrder qu co tf at f =
+        maybe testFm tf (asBool f)
+            where testFm = case f of
+                             Quant op v f' -> qu op v f'
+                             Combine x -> co x
+                             Predicate x -> at x
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Quant q1 v1 f1', Quant q2 v2 f2') -> qu q1 v1 (Quant q1 v1 f1') q2 v2 (Quant q2 v2 f2')
+          (Combine x, Combine y) -> co x y
+          (Predicate x, Predicate y) -> at x y
+          _ -> Nothing
+
+instance (Constants (Formula v p f),
+          Variable v, Ord v, Data v, Show v,
+          Arity p, Constants p, Ord p, Data p, Show p,
+          Skolem f, Ord f, Data f, Show f) => Literal (Formula v p f) (Predicate p (PTerm v f)) v where
+    foldLiteral co tf at l =
+        case l of
+          (Combine ((:~:) x)) -> co x
+          (Predicate p) -> at p
+          _ -> error "Literal (Formula v p f)"
+    atomic = Predicate
+-}
+
+instance (Constants p, Ord v, Ord p, Ord f, Constants (Predicate p (PTerm v f)), C.Formula (Formula v p f) (Predicate p (PTerm v f))
+         ) => Literal (Formula v p f) (Predicate p (PTerm v f)) where
+    foldLiteral neg tf at f =
+        case f of
+          Quant _ _ _ -> error "Invalid literal"
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error "Invalid literal"
+          Predicate p -> if p == fromBool True
+                         then tf True
+                         else if p == fromBool False
+                              then tf False
+                              else at p
+
+instance (C.Predicate p, Variable v, Function f v) => C.Atom (Predicate p (PTerm v f)) (PTerm v f) v where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (Apply _ ts) = foldr f r ts
+    foldTerms f r (Equal t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
+
+instance (Variable v, Pretty v,
+          C.Predicate p, Pretty p,
+          Function f v, Pretty f) => Pretty (Predicate p (PTerm v f)) where
+    pretty atom = prettyAtomEq pretty pretty pretty 0 atom
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          C.Predicate p, Variable v, Function f v, HasFixity (Predicate p (PTerm v f))) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v) => Pretty (Formula v p f) where
+    pretty f = prettyFirstOrder (\ _ -> pretty) pretty 0 $ f
+
+instance HasFixity (Predicate p term) where
+    fixity = const botFixity
+
+$(deriveSafeCopy 1 'base ''PTerm)
+$(deriveSafeCopy 1 'base ''Formula)
+$(deriveSafeCopy 2 'extension ''Predicate)
+
+-- Migration --
+
+data Predicate_v1 p term
+    = Equal_v1 term term
+    | NotEqual_v1 term term
+    | Constant_v1 Bool
+    | Apply_v1 p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+$(deriveSafeCopy 1 'base ''Predicate_v1)
+
+instance (SafeCopy p, SafeCopy term) => Migrate (Predicate p term) where
+    type MigrateFrom (Predicate p term) = (Predicate_v1 p term)
+    migrate (Equal_v1 t1 t2) = Equal t1 t2
+    migrate (Apply_v1 p ts) = Apply p ts
+    migrate (NotEqual_v1 _ _) = error "Failure migrating Predicate NotEqual"
+    migrate (Constant_v1 _) = error "Failure migrating Predicate Constant"
diff --git a/testdata/logic/Data/Logic/Types/FirstOrderPublic.hs b/testdata/logic/Data/Logic/Types/FirstOrderPublic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/FirstOrderPublic.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS -Wwarn -fno-warn-orphans #-}
+-- |An instance of FirstOrderFormula which implements Eq and Ord by comparing
+-- after conversion to normal form.  This helps us notice that formula which
+-- only differ in ways that preserve identity, e.g. swapped arguments to a
+-- commutative operator.
+
+module Data.Logic.Types.FirstOrderPublic
+    ( Formula(..)
+    , Bijection(..)
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, fixityFirstOrder)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(fixity))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Normal.Implicative (implicativeNormalForm, ImplicativeForm, runNormal)
+import qualified Data.Logic.Types.FirstOrder as N
+import Data.SafeCopy (base, deriveSafeCopy)
+import Data.Set (Set)
+import Data.Typeable (Typeable)
+
+-- |Convert between the public and internal representations.
+class Bijection p i where
+    public :: i -> p
+    intern :: p -> i
+
+-- |The new Formula type is just a wrapper around the Native instance
+-- (which eventually should be renamed the Internal instance.)  No
+-- derived Eq or Ord instances.
+data Formula v p f = Formula {unFormula :: N.Formula v p f} deriving (Data, Typeable, Show)
+
+instance Bijection (Formula v p f) (N.Formula v p f) where
+    public = Formula
+    intern = unFormula
+
+instance Bijection (Combination (Formula v p f)) (Combination (N.Formula v p f)) where
+    public (BinOp x op y) = BinOp (public x) op (public y)
+    public ((:~:) x) = (:~:) (public x)
+    intern (BinOp x op y) = BinOp (intern x) op (intern y)
+    intern ((:~:) x) = (:~:) (intern x)
+
+instance Negatable (Formula v p f) where
+    negatePrivate = Formula . negatePrivate . unFormula
+    foldNegation normal inverted = foldNegation (normal . Formula) (inverted . Formula) . unFormula
+
+instance (Constants (N.Formula v p f), Predicate p, Variable v, Function f v) => Constants (Formula v p f) where
+    fromBool = Formula . fromBool
+    asBool = asBool . unFormula
+
+instance (C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Constants (Formula v p f),
+          Constants (N.Formula v p f),
+          Variable v, Predicate p, Function f v) => Combinable (Formula v p f) where
+    x .<=>. y = Formula $ (unFormula x) .<=>. (unFormula y)
+    x .=>.  y = Formula $ (unFormula x) .=>. (unFormula y)
+    x .|.   y = Formula $ (unFormula x) .|. (unFormula y)
+    x .&.   y = Formula $ (unFormula x) .&. (unFormula y)
+
+instance (Predicate p, Function f v) => C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    atomic = Formula . C.atomic
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v where
+    for_all v x = public $ for_all v (intern x :: N.Formula v p f)
+    exists v x = public $ exists v (intern x :: N.Formula v p f)
+    foldFirstOrder qu co tf at f = foldFirstOrder qu' co' tf at (intern f :: N.Formula v p f)
+        where qu' quant v form = qu quant v (public form)
+              co' x = co (public x)
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Show v, Show p, Show f, HasFixity (Formula v p f), Variable v, Predicate p,
+          Function f v) => PropositionalFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    foldPropositional co tf at f = foldPropositional co' tf at (intern f :: N.Formula v p f)
+        where co' x = co (public x)
+
+-- |Here are the magic Ord and Eq instances
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v) => Ord (Formula v p f) where
+    compare a b =
+        let (a' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula a))
+            (b' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula b)) in
+        case compare a' b' of
+          EQ -> EQ
+          x -> {- if isRenameOf a' b' then EQ else -} x
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v, Constants (N.Predicate p (N.PTerm v f)),
+          FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v) => Eq (Formula v p f) where
+    a == b = compare a b == EQ
+
+instance (Predicate p, Function f v) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Pretty v, Show v, Variable v,
+          Pretty p, Show p, Predicate p,
+          Pretty f, Show f, Function f v) => Pretty (Formula v p f) where
+    pretty formula = prettyFirstOrder (\ _prec a -> pretty a) pretty 0 formula
+
+$(deriveSafeCopy 1 'base ''Formula)
diff --git a/testdata/logic/Data/Logic/Types/Harrison/Equal.hs b/testdata/logic/Data/Logic/Types/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Harrison/Equal.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Types.Harrison.Equal where
+
+-- ========================================================================= 
+-- First order logic with equality.                                          
+--                                                                           
+-- Copyright (co) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import qualified Data.Logic.Classes.Atom as C
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool)
+import Data.Logic.Classes.Equals (AtomEq(..), showFirstOrderFormulaEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (fixityFirstOrder, mapAtomsFirstOrder, foldAtomsFirstOrder)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import qualified Data.Logic.Classes.Propositional as P
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (isRenameOfAtomEq, getSubstAtomEq)
+import Data.Logic.Types.Harrison.FOL (TermType(..))
+import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import Data.String (IsString(..))
+import Text.PrettyPrint (text, cat)
+
+data FOLEQ = EQUALS TermType TermType | R String [TermType] deriving (Eq, Ord, Show)
+data PredName = (:=:) | Named String deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Arity PredName where
+    arity (:=:) = Just 2
+    arity _ = Nothing
+
+instance Show (Formula FOLEQ) where
+    show = showFirstOrderFormulaEq
+
+instance HasFixity FOLEQ where
+    fixity (EQUALS _ _) = Fixity 5 InfixL
+    fixity _ = Fixity 10 InfixN
+
+instance IsString PredName where
+    fromString "=" = (:=:)
+    fromString s = Named s
+
+instance Constants PredName where
+    fromBool True = Named "true"
+    fromBool False = Named "false"
+    asBool x
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOLEQ where
+    fromBool x = R (fromBool x) []
+    asBool (R p _)
+        | fromBool True == p = Just True
+        | fromBool False == p = Just False
+        | True = Nothing
+    asBool _ = Nothing
+
+instance Predicate PredName
+
+instance Pretty PredName where
+    pretty (:=:) = text "="
+    pretty (Named s) = text s
+
+-- | Using PredName for the predicate type is not quite appropriate
+-- here, but we need to implement this instance so we can use it as a
+-- superclass of AtomEq below.
+instance Apply FOLEQ PredName TermType where
+    foldApply f _ (EQUALS t1 t2) = f (:=:) [t1, t2]
+    foldApply f tf (R p ts) = maybe (f (Named p) ts) tf (asBool (Named p))
+    apply' (Named p) ts = R p ts
+    apply' (:=:) [t1, t2] = EQUALS t1 t2
+    apply' (:=:) _ = error "arity"
+
+{-
+instance FirstOrderFormula (Formula FOLEQ) FOLEQ String where
+    exists = Exists
+    for_all = Forall
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+    atomic = Atom
+-}
+
+instance C.Formula (Formula FOLEQ) FOLEQ => P.PropositionalFormula (Formula FOLEQ) FOLEQ where
+    foldPropositional co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall _ _ -> error "quantifier in propositional formula"
+          Exists _ _ -> error "quantifier in propositional formula"
+
+instance Pretty FOLEQ where
+    pretty (EQUALS a b) = cat [pretty a, pretty (:=:), pretty b]
+    pretty (R s ts) = cat ([pretty s, pretty "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance HasFixity (Formula FOLEQ) where
+    fixity = fixityFirstOrder
+
+instance C.Formula (Formula FOLEQ) FOLEQ => Literal (Formula FOLEQ) FOLEQ where
+    foldLiteral neg tf at lit =
+        case lit of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> neg fm'
+          _ -> error "Literal (Formula FOLEQ)"
+
+-- instance PredicateEq PredName where
+--     eqp = (:=:)
+
+instance AtomEq FOLEQ PredName TermType where
+    foldAtomEq pr tf _ (R p ts) = maybe (pr (Named p) ts) tf (asBool (Named p))
+    foldAtomEq _ _ eq (EQUALS t1 t2) = eq t1 t2
+    equals = EQUALS
+    applyEq' (Named s) ts = R s ts
+    applyEq' (:=:) [t1, t2] = EQUALS t1 t2
+    applyEq' _ _ = error "arity"
+
+instance C.Atom FOLEQ TermType String where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (R _ ts) = foldr f r ts
+    foldTerms f r (EQUALS t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
diff --git a/testdata/logic/Data/Logic/Types/Harrison/FOL.hs b/testdata/logic/Data/Logic/Types/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Harrison/FOL.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Data.Logic.Types.Harrison.FOL
+    ( TermType(..)
+    , FOL(..)
+    , Function(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+--import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool)
+--import Data.Logic.Classes.FirstOrder (foldAtomsFirstOrder, mapAtomsFirstOrder)
+--import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Term(vt, foldTerm, fApp))
+import qualified Data.Logic.Classes.Term as C
+--import qualified Data.Logic.Classes.FirstOrder as C
+--import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import qualified Data.Logic.Types.Common ({- instance Variable String -})
+import Prelude hiding (pred)
+import Text.PrettyPrint (text, cat)
+
+-- -------------------------------------------------------------------------
+-- Terms.                                                                   
+-- -------------------------------------------------------------------------
+
+data TermType
+    = Var String
+    | Fn Function [TermType]
+    deriving (Eq, Ord)
+
+data FOL = R String [TermType] deriving (Eq, Ord, Show)
+
+instance Show TermType where
+    show (Var v) = "vt " ++ show v
+    show (Fn f ts) = "fApp " ++ show f ++ " " ++ show ts
+
+instance Pretty TermType where
+    pretty (Var v) = pretty v
+    pretty (Fn f ts) = cat ([pretty f, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Apply FOL String TermType where
+    foldApply f tf (R p ts) = maybe (f p ts) tf (asBool p)
+    apply' = R
+
+-- | This is probably dangerous.
+instance Constants String where
+    fromBool True = "true"
+    fromBool False = "false"
+    asBool x 
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOL where
+    fromBool x = R (fromBool x) []
+    asBool (R p _) = asBool p
+
+instance Predicate String
+
+{-
+instance Pretty String where
+    pretty = text
+
+instance FirstOrderFormula (Formula FOL) FOL String where
+    -- type C.Term (Formula FOL) = Term
+    -- type V (Formula FOL) = String
+    -- type Pr (Formula FOL) = String
+    -- type Fn (Formula FOL) = String -- ^ Atomic function type
+
+    -- quant C.Exists v fm = H.Exists v fm
+    -- quant C.Forall v fm = H.Forall v fm
+    for_all = H.Forall
+    exists = H.Exists
+    atomic = Atom
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          H.Forall v fm' -> qu C.Forall v fm'
+          H.Exists v fm' -> qu C.Exists v fm'
+-}
+
+instance Pretty FOL where
+    pretty (R p ts) = cat ([pretty p, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Arity String where
+    arity _ = Nothing
+
+-- | The Harrison book uses String for atomic function, but we need
+-- something a little more type safe because of our Skolem class.
+data Function
+    = FName String
+    | Skolem String
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+instance Pretty Function where
+    pretty (FName s) = text s
+    pretty (Skolem v) = text ("sK" ++ v)
+
+instance C.Function Function String
+
+instance Skolem Function String where
+    toSkolem = Skolem
+    isSkolem (Skolem _) = True
+    isSkolem _ = False
+
+instance Term TermType String Function where
+    -- type V Term = String
+    -- type Fn Term = String
+    vt = Var
+    fApp = Fn
+    foldTerm vfn _ (Var x) = vfn x
+    foldTerm _ ffn (Fn f ts) = ffn f ts
+    zipTerms = undefined
+
+instance HasFixity FOL where
+    fixity = const (Fixity 10 InfixN)
diff --git a/testdata/logic/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs b/testdata/logic/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.FirstOrder
+    ( Formula(..)
+    ) where
+
+--import Data.Char (isDigit)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder)
+import qualified Data.Logic.Classes.FirstOrder as C
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity)
+import Data.Logic.Types.Common ({- instance Variable String -})
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    | Forall String (Formula a)
+    | Exists String (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Constants a, Pretty a, HasFixity a) => C.Formula (Formula a) a where
+    atomic = Atom
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula a) a, Constants a, Pretty a, HasFixity a) => FirstOrderFormula (Formula a) a String where
+    for_all = Forall
+    exists = Exists
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+
+instance (FirstOrderFormula (Formula a) a String) => Pretty (Formula a) where
+    pretty = prettyFirstOrder (const pretty) pretty 0
diff --git a/testdata/logic/Data/Logic/Types/Harrison/Formulas/Propositional.hs b/testdata/logic/Data/Logic/Types/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.Propositional
+    ( Formula(..)
+    ) where
+
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), topFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..), prettyPropositional, fixityPropositional, foldAtomsPropositional, mapAtomsPropositional)
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (Formula atom), Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    -- The atom type for this formula is the same as its first type parameter.
+    foldPropositional co tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> co ((:~:) f)
+          And f g -> co (BinOp f (:&:) g)
+          Or f g -> co (BinOp f (:|:) g)
+          Imp f g -> co (BinOp f (:=>:) g)
+          Iff f g -> co (BinOp f (:<=>:) g)
+          Atom x -> at x
+
+instance (HasFixity atom, Pretty atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> neg f
+          Atom x -> at x
+          _ -> error ("Unexpected literal " ++ show (pretty formula))
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/logic/Data/Logic/Types/Harrison/Prop.hs b/testdata/logic/Data/Logic/Types/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Harrison/Prop.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Prop
+    ( Prop(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Pretty
+import Data.Logic.Classes.Propositional (showPropositional)
+import Data.Logic.Types.Harrison.Formulas.Propositional (Formula(..))
+import Prelude hiding (negate)
+import Text.PrettyPrint (text)
+
+-- =========================================================================
+-- Basic stuff for propositional logic: datatype, parsing and printing.     
+-- =========================================================================
+
+newtype Prop = P {pname :: String} deriving (Read, Data, Typeable, Eq, Ord)
+
+instance Show Prop where
+    show x = "P " ++ show (pname x)
+
+instance Pretty Prop where
+    pretty = text . pname
+
+instance HasFixity String where
+    fixity = const botFixity
+
+instance HasFixity Prop where
+    fixity = const botFixity
+
+instance Show (Formula Prop) where
+    show = showPropositional show
+
+instance Show (Formula String) where
+    show = showPropositional show
diff --git a/testdata/logic/Data/Logic/Types/Propositional.hs b/testdata/logic/Data/Logic/Types/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/logic/Data/Logic/Types/Propositional.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+module Data.Logic.Types.Propositional where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(..), asBool)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), topFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..), prettyPropositional, fixityPropositional, foldAtomsPropositional, mapAtomsPropositional)
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula atom
+    = Combine (Combination (Formula atom))
+    | Atom atom
+    | T
+    | F
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq,Ord,Data,Typeable)
+
+instance Negatable (Formula atom) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Ord atom) => Combinable (Formula atom) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+
+instance Constants (Formula atom) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error ("Unexpected literal: " ++ show (pretty formula))
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (C.Formula (Formula atom) atom, Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    foldPropositional co tf at formula =
+        case formula of
+          Combine x -> co x
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/merge1-expected/Debian/Repo/Cache.hs b/testdata/merge1-expected/Debian/Repo/Cache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Cache.hs
@@ -0,0 +1,516 @@
+{- This is a comment at the top -}   
+{-# LANGUAGE DeriveDataTypeable, PackageImports #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |An AptCache represents a local cache of a remote repository.  The
+-- cached information is usually downloaded by running "apt-get
+-- update", and appears in @\/var\/lib\/apt\/lists@.
+module Debian.Repo.Cache {-# WARNING "this is a warning" #-}
+    ( SourcesChangedAction(..)
+    , aptSourcePackagesSorted
+    , sliceIndexes
+    , cacheDistDir
+    , distDir
+    , aptDir
+    , cacheRootDir
+    , cacheSourcesPath
+    , sourcesPath
+    , sourceDir
+    , aptCacheFiles
+    , aptCacheFilesOfSlice
+    , archFiles
+    , buildArchOfEnv
+    , buildArchOfRoot
+    , updateCacheSources
+    , sourcePackages
+    , binaryPackages
+    , runAptGet
+    , aptOpts
+    , getSourcePackagesBase
+    , getBinaryPackagesBase
+    , getSourcePackagesBuild
+    , getBinaryPackagesBuild
+    , prepareAptEnv
+    , updateAptEnv
+    ) where
+
+
+
+import Control.DeepSeq (force, NFData)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Data (Data)
+import Data.List (intercalate, sortBy)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Relation (BinPkgName, PkgName, SrcPkgName(..))
+import Debian.Release (ReleaseName(relName), releaseName', sectionName')
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.Package (binaryPackagesOfCachedIndex, sourcePackagesOfCachedIndex)
+import Debian.Repo.Slice (binarySlices, sourceSlices, verifySourcesList)
+import Debian.Repo.SourcesList (parseSourcesList)
+import Debian.Repo.Types.AptBuildCache (AptBuildCache(aptSliceList))
+import Debian.Repo.Types.AptCache (AptCache(..), AptCache(aptArch, aptBaseSliceList, aptBinaryPackages, aptReleaseName, aptSourcePackages, globalCacheDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(..), EnvRoot(EnvRoot))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID), binaryPackageName, PackageID(packageVersion), PackageIndex(..), SourcePackage(sourcePackageID), sourcePackageName)
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (Repo(repoReleaseInfo), repoKey, RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache, prepareRepository)
+import Debian.Repo.Types.Slice (Slice(..), SliceList(slices))
+import Debian.Sources (DebSource(..), SourceType(..))
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Extra.Files (replaceFile)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetLine, stdin)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Progress (ePutStr, ePutStrLn, qPutStrLn, runProcessF)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+
+
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Function (on)
+import Data.List (sortBy)
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Monads.Apt (insertAptImage, lookupAptImage, MonadApt(getApt, putApt))
+import Debian.Repo.Types.AptImage (AptImage(..))
+import Debian.Repo.Types.EnvPath (EnvRoot(..))
+import Debian.Repo.Types.PackageIndex (PackageID(packageVersion), SourcePackage(sourcePackageID))
+import Debian.Repo.Types.Slice (NamedSliceList(sliceList, sliceListName))
+import Debian.Sources (SliceName(sliceName))
+import Extra.Files (replaceFile, writeFileIfMissing)
+import System.Directory (createDirectoryIfMissing)
+import System.Process (shell)
+import System.Process.Progress (qPutStrLn, quieter, runProcessF)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+
+instance NFData ExitCode
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+-- | A directory which will hold all the cached files for this
+-- NamedSliceList.
+cacheDistDir :: FilePath -> ReleaseName -> FilePath
+cacheDistDir cacheDir release = cacheDir ++ "/dists/" ++ relName release
+
+cacheRootDir :: FilePath -> ReleaseName -> EnvRoot
+cacheRootDir cacheDir release = EnvRoot (cacheDistDir cacheDir release ++ "/aptEnv")
+
+distDir :: AptCache c => c -> FilePath
+distDir cache = cacheDistDir (globalCacheDir cache) (aptReleaseName cache)
+
+aptDir :: AptCache c => c -> SrcPkgName -> FilePath
+aptDir cache package = distDir cache ++ "/apt/" ++ unSrcPkgName package
+
+-- | The path where a text of the SliceList is stored.
+cacheSourcesPath :: FilePath -> ReleaseName -> FilePath
+cacheSourcesPath cacheDir release = cacheDistDir cacheDir release </> "sources"
+
+sourcesPath :: AptCache c => c -> FilePath
+sourcesPath cache = cacheSourcesPath (globalCacheDir cache) (aptReleaseName cache)
+
+-- Additional functions which can only be used on already constructed
+-- instances of AptCache.
+
+-- | A directory holding all files downloaded by apt-get source for a
+-- certain package
+sourceDir :: AptCache t => t -> String -> FilePath
+sourceDir c package = distDir c ++ "/apt/" ++ package
+
+-- |Return all the named source packages sorted by version
+aptSourcePackagesSorted :: AptCache t => t -> [SrcPkgName] -> [SourcePackage]
+aptSourcePackagesSorted os names =
+    sortBy cmp . filterNames names . aptSourcePackages $ os
+    where
+      filterNames names' packages =
+          filter (flip elem names' . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: (MonadRepoCache m, AptCache a) => a -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes cache slice =
+    prepareRepository (sliceRepoKey slice) >>= \ repo ->
+    case (sourceDist (sliceSource slice)) of
+      Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+      Right (release, sections) -> return $ map (makeIndex repo release) sections
+    where
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> aptArch cache })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+-- |Return the paths in the local cache of the index files of a slice list.
+aptCacheFiles :: AptCache a => a -> [DebSource] -> [FilePath]
+aptCacheFiles apt sources = concat . map (aptCacheFilesOfSlice apt) $ sources
+
+-- |Return the paths in the local cache of the index files of a single slice.
+aptCacheFilesOfSlice :: AptCache a => a -> DebSource -> [FilePath]
+aptCacheFilesOfSlice apt slice = archFiles (aptArch apt) slice
+
+-- |Return the list of files that apt-get update would write into
+-- \/var\/lib\/apt\/lists when it processed the given list of DebSource.
+archFiles :: Arch -> DebSource -> [FilePath]
+archFiles arch deb =
+    case (arch, deb) of
+      (Binary _ _, DebSource DebSrc _ _) ->
+          map (++ "_source_Sources") (archFiles' deb)
+      (Binary _os _cpu, DebSource Deb _ _) ->
+          map (++ ("_binary-" ++ show (prettyArch arch) ++ "_Packages")) (archFiles' deb)
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+archFiles' :: DebSource -> [FilePath]
+archFiles' deb =
+    let uri = sourceUri deb
+        distro = sourceDist deb in
+    let scheme = uriScheme uri
+        auth = uriAuthority uri
+        path = uriPath uri in
+    let userpass = maybe "" uriUserInfo auth
+        reg = maybeOfString $ maybe "" uriRegName auth
+        port = maybe "" uriPort auth in
+    let (user, pass) = break (== ':') userpass in
+    let user' = maybeOfString user
+        pass' = maybeOfString pass in
+    let uriText = prefix scheme user' pass' reg port path in
+    -- what about dist?
+    either (\ exact -> [(escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText ++ escape exact))])
+           (\ (dist, sections) ->
+                map (\ section ->
+                         (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+                          releaseName' dist ++ "_" ++ sectionName' section))
+                    sections)
+           distro
+    where
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid DebSource: " ++ show (pretty deb))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+
+buildArchOfEnv :: EnvRoot -> IO Arch
+buildArchOfEnv (EnvRoot root)  =
+    do setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+       a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (ArchOS os) (ArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+{-
+  (err, _) <- useEnv root forceList (readProcessChunks (shell cmd) L.empty) >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "export LOGNAME=root; dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+{-
+    do (code, out, err, _) <- runProcess (shell cmd) L.empty >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s =
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.
+updateCacheSources :: (MonadApt m, AptCache c) => SourcesChangedAction -> c -> m c
+updateCacheSources sourcesChangedAction distro
+  = qPutStrLn "Updating cache sources" >>
+      do let baseSources = aptBaseSliceList distro
+         let dir = Debian.Repo.Cache.distDir distro
+         distExists <- liftIO $
+                         doesFileExist (Debian.Repo.Cache.sourcesPath distro)
+         case distExists of
+             True -> do fileSources <- liftIO
+                                         (readFile (Debian.Repo.Cache.sourcesPath distro))
+                                         >>= verifySourcesList Nothing . parseSourcesList
+                        case (fileSources == baseSources, sourcesChangedAction) of
+                            (True, _) -> return ()
+                            (False, SourcesChangedError) -> do ePutStrLn
+                                                                 ("The sources.list in the existing '"
+                                                                    ++
+                                                                    relName (aptReleaseName distro)
+                                                                      ++
+                                                                      "' build environment doesn't match the parameters passed to the autobuilder"
+                                                                        ++
+                                                                        ":\n\n" ++
+                                                                          Debian.Repo.Cache.sourcesPath
+                                                                            distro
+                                                                            ++
+                                                                            ":\n\n" ++
+                                                                              show
+                                                                                (pretty fileSources)
+                                                                                ++
+                                                                                "\nRun-time parameters:\n\n"
+                                                                                  ++
+                                                                                  show
+                                                                                    (pretty
+                                                                                       baseSources)
+                                                                                    ++
+                                                                                    "\n" ++
+                                                                                      "It is likely that the build environment in\n"
+                                                                                        ++
+                                                                                        dir ++
+                                                                                          " is invalid and should be rebuilt.")
+                                                               ePutStr $
+                                                                 "Remove it and continue (or exit)?  [y/n]: "
+                                                               result <- liftIO $ hGetLine stdin
+                                                               case result of
+                                                                   ('y' : _) -> do liftIO $
+                                                                                     removeRecursiveSafely
+                                                                                       dir
+                                                                                   liftIO $
+                                                                                     createDirectoryIfMissing
+                                                                                       True
+                                                                                       dir
+                                                                                   liftIO $
+                                                                                     replaceFile
+                                                                                       (Debian.Repo.Cache.sourcesPath
+                                                                                          distro)
+                                                                                       (show
+                                                                                          (pretty
+                                                                                             baseSources))
+                                                                   _ -> error
+                                                                          ("Please remove " ++
+                                                                             dir ++ " and restart.")
+                            (False, RemoveRelease) -> do ePutStrLn $
+                                                           "Removing suspect environment: " ++ dir
+                                                         liftIO $ removeRecursiveSafely dir
+                                                         liftIO $ createDirectoryIfMissing True dir
+                                                         liftIO $
+                                                           replaceFile
+                                                             (Debian.Repo.Cache.sourcesPath distro)
+                                                             (show (pretty baseSources))
+                            (False, UpdateSources) -> do ePutStrLn $
+                                                           "Updating environment with new sources.list: "
+                                                             ++ dir
+                                                         liftIO $
+                                                           removeFile
+                                                             (Debian.Repo.Cache.sourcesPath distro)
+                                                         liftIO $
+                                                           replaceFile
+                                                             (Debian.Repo.Cache.sourcesPath distro)
+                                                             (show (pretty baseSources))
+             False -> do liftIO $ createDirectoryIfMissing True dir
+                         liftIO $
+                           replaceFile (Debian.Repo.Cache.sourcesPath distro)
+                             (show (pretty baseSources))
+         return distro
+
+-- | Return a sorted list of available source packages, newest version first.
+sourcePackages :: AptCache a => a -> [SrcPkgName] -> [SourcePackage]
+sourcePackages os names =
+    sortBy cmp . filterNames . aptSourcePackages $ os
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          filter (flip elem names . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+binaryPackages :: AptCache a => a -> [BinPkgName] -> [BinaryPackage]
+binaryPackages os names =
+    sortBy cmp . filterNames . aptBinaryPackages $ os
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          filter (flip elem names . binaryPackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
+
+getSourcePackagesBase :: (AptCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getSourcePackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBase :: (AptCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+-- | Note that apt-get source works for binary or source package names.
+runAptGet :: (PkgName n, AptCache t) => t -> FilePath -> String -> [(n, Maybe DebianVersion)] -> IO ()
+runAptGet os dir command packages =
+    createDirectoryIfMissing True dir >> runProcessF (shell cmd) L.empty >> return ()
+    where
+      cmd = (intercalate " " ("cd" : dir : "&&" : "apt-get" : aptOpts os : command : map formatPackage packages))
+      formatPackage (name, Nothing) = show (pretty name)
+      formatPackage (name, Just version) = show (pretty name) ++ "=" ++ show (prettyDebianVersion version)
+
+aptOpts :: AptCache t => t -> String
+aptOpts os =
+    (" -o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status" ++
+     " -o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists" ++
+     " -o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives" ++
+     " -o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list" ++
+     " -o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d")
+    where root = rootPath . rootDir $ os
+
+
+prepareAptEnv :: MonadApt m =>
+                 FilePath               -- Put environment in a subdirectory of this
+              -> SourcesChangedAction   -- What to do if environment already exists and sources.list is different
+              -> NamedSliceList         -- The sources.list
+              -> m AptImage             -- The resulting environment
+prepareAptEnv cacheDir sourcesChangedAction sources =
+    (\ x -> qPutStrLn ("Preparing apt-get environment for " ++ show (sliceName (sliceListName sources))) >> quieter 2 x) $
+    getApt >>= return . lookupAptImage (sliceListName sources) >>=
+    maybe (prepareAptEnv' cacheDir sourcesChangedAction sources) return
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+{-# NOINLINE prepareAptEnv' #-}
+prepareAptEnv' :: MonadApt m => FilePath -> SourcesChangedAction -> NamedSliceList -> m AptImage
+prepareAptEnv' cacheDir sourcesChangedAction sources =
+    do let root = rootPath (cacheRootDir cacheDir (ReleaseName (sliceName (sliceListName sources))))
+       --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/cache/apt/archives/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/dpkg")
+       liftIO $ createDirectoryIfMissing True (root ++ "/etc/apt")
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/status") ""
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/diversions") ""
+       -- We need to create the local pool before updating so the
+       -- sources.list will be valid.
+       let sourceListText = show (pretty (sliceList sources))
+       -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+       liftIO $ replaceFile (root ++ "/etc/apt/sources.list") sourceListText
+       arch <- liftIO $ buildArchOfRoot
+       let os = AptImage { aptGlobalCacheDir = cacheDir
+                         , aptImageRoot = EnvRoot root
+                         , aptImageArch = arch
+                         , aptImageReleaseName = ReleaseName . sliceName . sliceListName $ sources
+                         , aptImageSliceList = sliceList sources
+                         , aptImageSourcePackages = []
+                         , aptImageBinaryPackages = [] }
+       os' <- updateCacheSources sourcesChangedAction os >>= updateAptEnv
+       getApt >>= putApt . insertAptImage (sliceListName sources) os'
+       return os'
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+{-# NOINLINE updateAptEnv #-}
+updateAptEnv :: MonadApt m => AptImage -> m AptImage
+updateAptEnv os =
+    liftIO (runProcessF (shell cmd) L.empty) >>
+    getSourcePackagesBase os >>= return . sortBy cmp >>= \ sourcePackages ->
+    getBinaryPackagesBase os >>= \ binaryPackages ->
+    return $ os { aptImageSourcePackages = sourcePackages
+                , aptImageBinaryPackages = binaryPackages }
+    where
+      cmd = "apt-get" ++ aptOpts os ++ " update"
+      -- Flip args to get newest version first
+      cmp = flip (compare `on` (packageVersion . sourcePackageID))
+{-
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+    putStrLn ("> " ++ cmd) >> system cmd >>= \ code ->
+    case code of
+      ExitSuccess -> return ()
+      ExitFailure n -> error $ cmd ++ " -> ExitFailure " ++ show n
+-}
+
diff --git a/testdata/merge1-expected/Debian/Repo/Monads/Apt.hs b/testdata/merge1-expected/Debian/Repo/Monads/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Monads/Apt.hs
@@ -0,0 +1,195 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Debian.Repo.Monads.Apt
+    ( MonadApt(..)
+    , AptIOT
+    , AptIO
+    -- * AptIO Monad
+    , runAptIO
+    , runAptT
+    , tryAB
+    , tryJustAB
+    -- * State
+    , AptState
+    , initState
+    , setRepoMap
+    , getRepoMap
+    , lookupRepository
+    , insertRepository
+    , lookupAptImage
+    , insertAptImage
+    , lookupSourcePackages
+    , insertSourcePackages
+    , lookupBinaryPackages
+    , insertBinaryPackages
+    , readParagraphs
+    , findRelease
+    , putRelease
+    , countTasks
+    ) where
+
+import Control.Exception (try)
+import Control.Exception as E (Exception, tryJust)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, mapStateT, MonadIO(..), MonadTrans(..), put, StateT(runStateT))
+import qualified Data.Map as Map (empty, insert, lookup, Map)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(parseControlFromHandle), Paragraph)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Repo (Repo(repoKey), RepoKey(..))
+import Debian.Repo.Types.Repository (MonadRepoCache(getRepoCache, putRepoCache), Repository)
+import Debian.Sources (SliceName)
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+import System.Process.Progress (ePutStrLn)
+import Text.Printf (printf)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+type AptIO = AptIOT IO
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository           -- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage       -- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage]) -- ^ The contents of a package index, and status of local cache of index file
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+-- |Perform an AptIO monad task in the IO monad.
+runAptIO :: AptIO a -> IO a
+runAptIO action = (runStateT action) initState >>= \ (a, _) -> return a
+
+runAptT :: Monad m => AptIOT m a -> m a
+runAptT action = (runStateT action) initState >>= return . fst
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryAB :: Exception e => AptIO a -> AptIO (Either e a)
+tryAB task =
+    do state <- get
+       mapStateT (try' state) task
+    where
+      try' state task' =
+          do result <- try task'
+             case result of
+               Left e -> return (Left e, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryJustAB :: Exception e => (e -> Maybe b) -> AptIO a -> AptIO (Either b a)
+tryJustAB f task =
+    do state <- get
+       mapStateT (tryJust' state) task
+    where
+      tryJust' state task' =
+          do result <- tryJust f task'
+             case result of
+               Left b -> return (Left b, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+setRepoMap :: Map.Map RepoKey Repository -> AptState -> AptState
+setRepoMap m state = state {repoMap = m}
+
+getRepoMap :: AptState -> Map.Map RepoKey Repository
+getRepoMap state = repoMap state
+
+lookupRepository :: RepoKey -> AptState -> Maybe Repository
+lookupRepository uri state = Map.lookup uri (repoMap state)
+
+insertRepository :: RepoKey -> Repository -> AptState -> AptState
+insertRepository uri repo state = state {repoMap = Map.insert uri repo (repoMap state)}
+
+lookupAptImage :: SliceName -> AptState -> Maybe AptImage
+lookupAptImage name state = Map.lookup  name (aptImageMap state)
+
+insertAptImage :: SliceName -> AptImage -> AptState -> AptState
+insertAptImage name image state = state {aptImageMap = Map.insert name image (aptImageMap state)}
+
+lookupSourcePackages :: FilePath -> AptState -> Maybe (FileStatus, [SourcePackage])
+lookupSourcePackages key state = Map.lookup key (sourcePackageMap state)
+
+insertSourcePackages :: FilePath -> (FileStatus, [SourcePackage]) -> AptState -> AptState
+insertSourcePackages key packages state = state {sourcePackageMap = Map.insert key packages (sourcePackageMap state)}
+
+lookupBinaryPackages :: FilePath -> AptState -> Maybe (FileStatus, [BinaryPackage])
+lookupBinaryPackages key state = Map.lookup key (binaryPackageMap state)
+
+insertBinaryPackages :: FilePath -> (FileStatus, [BinaryPackage]) -> AptState -> AptState
+insertBinaryPackages key packages state =
+    state {binaryPackageMap = Map.insert key packages (binaryPackageMap state)}
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)                 -- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")     -- Debugging output
+       return paragraphs
+
+findRelease :: Repository -> ReleaseName -> AptState -> Maybe Release
+findRelease repo dist state =
+    Map.lookup (repoKey repo, dist) (releaseMap state)
+
+putRelease :: Repository -> ReleaseName -> Release -> AptState -> AptState
+putRelease repo dist release state =
+    state {releaseMap = Map.insert (repoKey repo, dist) release (releaseMap state)}
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/merge1-expected/Debian/Repo/Monads/Top.hs b/testdata/merge1-expected/Debian/Repo/Monads/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Monads/Top.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Monads.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    ) where
+
+import Control.Monad.Reader (MonadReader(ask), ReaderT(runReaderT))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class Monad m => MonadTop m where
+    askTop :: m FilePath
+
+instance Monad m => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
diff --git a/testdata/merge1-expected/Debian/Repo/Orphans.hs b/testdata/merge1-expected/Debian/Repo/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Orphans.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Orphans where
+
+import Data.Text (Text)
+import qualified Debian.Control.Text as T
+
+deriving instance Show (T.Field' Text)
+deriving instance Ord (T.Field' Text)
+deriving instance Show T.Paragraph
+deriving instance Ord T.Paragraph
diff --git a/testdata/merge1-expected/Debian/Repo/Package.hs b/testdata/merge1-expected/Debian/Repo/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Package.hs
@@ -0,0 +1,367 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package
+    ( sourceFilePaths
+    , binaryPackageSourceVersion
+    , binarySourceVersion
+    , sourcePackageBinaryNames
+    , sourceBinaryNames
+    , toSourcePackage
+    , toBinaryPackage
+    , binaryPackageSourceID
+    , sourcePackageBinaryIDs
+    , sourcePackagesOfIndex
+    , sourcePackagesOfCachedIndex
+    , binaryPackagesOfIndex
+    , binaryPackagesOfCachedIndex
+    , getPackages
+    , putPackages
+    , releaseSourcePackages
+    , releaseBinaryPackages
+    , TH.FixityDirection(..)
+    , TH.Fixity(..)
+    ) where
+
+import Control.Exception as E (catch, ErrorCall(..), SomeException(..), try)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString, fromChunks)
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, intersperse, map, partition)
+import Data.Maybe (catMaybes)
+import Data.Set as Set (fromList, map, Set, unions)
+import qualified Data.Text as T (concat, pack, Text, unpack)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Control (ControlFunctions(asString, stripWS), formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(lookupP), Field, Field'(Field), fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Monads.Apt (insertBinaryPackages, insertSourcePackages, lookupBinaryPackages, lookupSourcePackages, MonadApt(getApt, putApt), readParagraphs)
+import Debian.Repo.PackageIndex (binaryIndexList, packageIndexPath, sourceIndexList)
+import Debian.Repo.Types.AptCache (AptCache(aptArch, rootDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(rootPath), outsidePath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(..), BinaryPackageLocal, binaryPackageName, makeBinaryPackageID, makeSourcePackageID, PackageID(..), PackageIndex(..), PackageIndexLocal, SourceControl(..), SourceFileSpec(SourceFileSpec, sourceFileName), SourcePackage(..))
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (RepoKey, repoKeyURI)
+import Debian.Repo.Types.Repository (LocalRepository, MonadRepoCache, repoRoot)
+import Debian.URI (fileFromURIStrict)
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Extra.Files (writeAndZipFileWithBackup)
+import qualified Language.Haskell.TH.Syntax as TH (FixityDirection(..), Fixity(..))
+import Network.URI (escapeURIString, URI(..), URIAuth(..), uriToString)
+import System.FilePath ((</>), takeDirectory)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+useConstants :: Constants p => p -> Maybe Bool
+useConstants x = asBool x
+
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
+
+sourceFilePaths :: SourcePackage -> Set FilePath
+sourceFilePaths package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = binaryPackageName package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
+
+-- |Return the name and version number of the source package that
+-- generated this binary package.
+-- see also: 'binaryPackageSourceVersion'
+binarySourceVersion :: Paragraph' T.Text -> Maybe ((BinPkgName, DebianVersion), (String, DebianVersion))
+binarySourceVersion paragraph =
+    let mBinaryName = fmap (BinPkgName . T.unpack) $ B.fieldValue "Package" paragraph
+        mBinaryVersion = fmap (parseDebianVersion . T.unpack) $ B.fieldValue "Version" paragraph
+    in
+      case (mBinaryName, mBinaryVersion) of
+        (Just binaryName, Just binaryVersion) ->
+            fmap ((,) (binaryName, binaryVersion)) $ binarySourceVersion' binaryName binaryVersion paragraph
+        _ -> Nothing
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
+
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
+
+sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+sourceBinaryNames paragraph =
+    case B.fieldValue "Binary" paragraph of
+      Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID (PackageIndex component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+
+sourcePackageBinaryIDs :: Arch -> PackageIndex -> SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs Source _ _ = error "invalid argument"
+sourcePackageBinaryIDs arch sourceIndex package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfCachedIndex :: (AptCache a, MonadApt m) => a -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupSourcePackages path state
+       status <- liftIO $ getFileStatus path `E.catch` (\ (_ :: IOError) -> error $ "Sources.list seems out of sync.  If a new release has been created you probably need to remove " ++ takeDirectory (rootPath (rootDir cache)) ++ " and try again - sorry about that.")
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toSourcePackage index) paragraphs
+                 putApt (insertSourcePackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfCachedIndex :: (MonadApt m, AptCache a) => a -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupBinaryPackages path state
+       status <- liftIO $ getFileStatus path
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toBinaryPackage release index) paragraphs
+                 putApt (insertBinaryPackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+-- | Return a list of all source packages.
+releaseSourcePackages :: MonadRepoCache m => RepoKey -> Release -> m (Set SourcePackage)
+releaseSourcePackages repo release =
+    mapM (sourcePackagesOfIndex repo release) (sourceIndexList release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages :: MonadRepoCache m => RepoKey -> Release -> m (Set BinaryPackage)
+releaseBinaryPackages repo release =
+    mapM (binaryPackagesOfIndex repo release) (binaryIndexList release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Write a set of packages into a package index.
+putPackages :: LocalRepository -> Release -> PackageIndexLocal ->  [BinaryPackageLocal] -> IO ()
+putPackages repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
diff --git a/testdata/merge1-expected/Debian/Repo/PackageIndex.hs b/testdata/merge1-expected/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,87 @@
+module Debian.Repo.PackageIndex
+    ( packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPathList
+    , packageIndexDirList
+    , packageIndexList
+    , sourceIndexList
+    , binaryIndexList
+    , releaseDir
+    , showIndexBrief
+    , debSourceFromIndex
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Types (PackageIndex(..), Release(..))
+import Debian.Repo.Types.Repo (repoURI)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (DebSource(..), SourceType(..))
+import System.FilePath ((</>))
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index ++ "/" ++ packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release ++ "/" ++ sectionName' (packageIndexComponent index) ++ "/source"
+      _ -> (releaseDir release ++ "/" ++
+            sectionName' (packageIndexComponent index) ++
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "/binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists/" ++ (releaseName' . releaseName $ release)
+
+packageIndexPathList :: Release -> [FilePath]
+packageIndexPathList release = map (packageIndexPath release) . packageIndexList $ release
+
+packageIndexDirList :: Release -> [FilePath]
+packageIndexDirList release = map (packageIndexDir release) . packageIndexList $ release
+
+packageIndexList :: Release -> [PackageIndex]
+packageIndexList release = sourceIndexList release ++ binaryIndexList release
+
+sourceIndexList :: Release -> [PackageIndex]
+sourceIndexList release =
+    map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexList :: Release -> [PackageIndex]
+binaryIndexList release =
+    concat . map componentIndexes $ (releaseComponents release)
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          map archIndex (filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+showIndexBrief :: (Repository, Release) -> PackageIndex -> String
+showIndexBrief release index =
+    (releaseName' . releaseName . snd $ release) </> sectionName' (packageIndexComponent index) </> showArch (packageIndexArch index)
+    where showArch Source = "source"
+          showArch All = "all"
+          showArch x@(Binary _ _) = "binary-" ++ show (prettyArch x)
+
+debSourceFromIndex :: (Repository, Release) -> PackageIndex -> DebSource
+debSourceFromIndex (repo, release) index =
+    DebSource {sourceType = typ,
+               sourceUri = repoURI repo,
+               sourceDist = Right (dist, components)}
+    where
+      typ = case arch of (Binary _ _) -> Deb; Source -> DebSrc; All -> Deb
+      arch = packageIndexArch index
+      dist = releaseName $ release
+      components = releaseComponents $ release
diff --git a/testdata/merge1-expected/Debian/Repo/Slice.hs b/testdata/merge1-expected/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Slice.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE PackageImports, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , verifySourceLine
+    , verifySourcesList
+    , repoSources
+    , parseNamedSliceList
+    , parseNamedSliceList'
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName)
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.SourcesList (parseSourceLine, parseSourcesList)
+import Debian.Repo.Types (EnvPath(..), EnvRoot(..))
+import Debian.Repo.Types.Repo (repoKey, RepoKey(..))
+import Debian.Repo.Types.Repository (prepareRepository)
+import Debian.Repo.Types.Slice (NamedSliceList(..), Slice(..), SliceList(..))
+import Debian.Sources (DebSource(..), SliceName(SliceName), SourceType(..))
+import Debian.URI (dirFromURI, fileFromURI, toURI')
+import Network.URI (URI(uriScheme, uriPath))
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+-- |Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadApt m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+parseNamedSliceList :: MonadApt m => (String, String) -> m (Maybe NamedSliceList)
+parseNamedSliceList (name, text) =
+    (verifySourcesList Nothing . parseSourcesList) text >>=
+    \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+-- |Create ReleaseCache info from an entry in the config file, which
+-- includes a dist name and the lines of the sources.list file.
+-- This also creates the basic 
+parseNamedSliceList' :: MonadApt m => (String, String) -> m NamedSliceList
+parseNamedSliceList' (name, text) =
+    do sources <- (verifySourcesList Nothing . parseSourcesList) text
+       return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+verifySourcesList :: MonadApt m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifySourceLine :: MonadApt m => Maybe EnvRoot -> String -> m Slice
+verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str)
+
+verifyDebSource :: MonadApt m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    repo >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      repo =
+          case uriScheme (sourceUri line) of
+            "file:" -> prepareRepository (Local (EnvPath chroot' (uriPath (sourceUri line))))
+            _ -> prepareRepository (Remote (toURI' (sourceUri line)))
+      chroot' = fromMaybe (EnvRoot "") chroot
diff --git a/testdata/merge1-expected/Debian/Repo/SourcesList.hs b/testdata/merge1-expected/Debian/Repo/SourcesList.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/SourcesList.hs
@@ -0,0 +1,142 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+--- | A DebSource represents a release of a remote repository and a
+--- method for accessing that repository.
+module Debian.Repo.SourcesList
+    (parseSourceLine,   -- String -> DebSource
+     parseSourceLine',  -- String -> Maybe DebSource
+     parseSourcesList,  -- String -> [DebSource]
+     quoteWords -- String -> [String]
+    )
+    where
+
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Sources (DebSource(..), SourceType(..))
+import Network.URI (parseURI, unEscapeString)
+
+{-
+
+deb uri distribution [component1] [componenent2] [...]
+
+The URI for the deb type must specify the base of the Debian
+distribution, from which APT will find the information it needs.
+
+distribution can specify an exact path, in which case the components
+must be omitted and distribution must end with a slash (/).
+
+If distribution does not specify an exact path, at least one component
+must be present.
+
+Distribution may also contain a variable, $(ARCH), which expands to
+the Debian architecture (i386, m68k, powerpc, ...)  used on the
+system.
+
+The rest of the line can be marked as a comment by using a #.
+
+Additional Notes:
+
+ + Lines can begin with leading white space.
+
+ + If the dist ends with slash (/), then it must be an absolute path
+   and it is an error to specify components after it.
+
+-}
+
+-- |quoteWords - similar to words, but with special handling of
+-- double-quotes and brackets.
+--
+-- The handling double quotes and [] is supposed to match:
+-- apt-0.6.44.2\/apt-pkg\/contrib\/strutl.cc:ParseQuoteWord()
+--
+-- The behaviour can be defined as:
+--
+--  Break the string into space seperated words ignoring spaces that
+--  appear between \"\" or []. Strip trailing and leading white space
+--  around words. Strip out double quotes, but leave the square
+--  brackets intact.
+quoteWords :: String -> [String]
+quoteWords [] = []
+quoteWords s = quoteWords' (dropWhile (==' ') s)
+    where
+      quoteWords' :: String -> [String]
+      quoteWords' [] = []
+      quoteWords' str =
+          case break (flip elem " [\"") str of
+            ([],[]) -> []
+            (w, []) -> [w]
+            (w, (' ':rest)) -> w : (quoteWords' (dropWhile (==' ') rest))
+            (w, ('"':rest)) ->
+                case break (== '"') rest of
+                  (w',('"':rest)) ->
+                      case quoteWords' rest of
+                        [] ->  [w ++ w']
+                        (w'':ws) -> ((w ++ w' ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing \" in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            (w, ('[':rest)) ->
+                case break (== ']') rest of
+                  (w',(']':rest)) ->
+                      case quoteWords' rest of
+                        []       -> [w ++ "[" ++ w' ++ "]"]
+                        (w'':ws) -> ((w ++ "[" ++ w' ++ "]" ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing ] in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            _ -> error ("the impossible happened in SourcesList.quoteWords")
+
+stripLine :: String -> String
+stripLine = takeWhile (/= '#') . dropWhile (== ' ')
+
+sourceLines :: String -> [String]
+sourceLines = filter (not . null) . map stripLine . lines
+
+-- |parseSourceLine -- parses a source line
+-- the argument must be a non-empty, valid source line with comments stripped
+-- see: 'sourceLines'
+parseSourceLine :: String -> DebSource
+parseSourceLine str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Deb
+                          "deb-src" -> DebSrc
+                          o -> error ("parseSourceLine: invalid type " ++ o ++ " in line:\n" ++ str)
+              theUri = case parseURI theUriStr of
+                         Nothing -> error ("parseSourceLine: invalid uri " ++ theUriStr ++ " in the line:\n" ++ str)
+                         Just u -> u
+              theDist = unEscapeString theDistStr
+          in
+            case last theDist of
+              '/' -> if null sections
+                      then DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              _ -> if null sections
+                    then error ("parseSourceLine: Dist is not an exact path, so at least one section is required on the line:\n" ++ str)
+                    else DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Right (parseReleaseName theDist, sections) }
+      _ -> error ("parseSourceLine: invalid line in sources.list:\n" ++ str)
+
+parseSourceLine' :: String -> Maybe DebSource
+parseSourceLine' str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Just Deb
+                          "deb-src" -> Just DebSrc
+                          _ -> Nothing
+              theUri = case parseURI theUriStr of
+                         Nothing -> Nothing
+                         Just u -> Just u
+              theDist = unEscapeString theDistStr
+          in
+            case (last theDist, theType, theUri) of
+              ('/', Just typ, Just uri) -> if null sections
+                      then Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              (_, Just typ, Just uri) -> if null sections
+                    then Nothing
+                    else Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Right ((parseReleaseName theDist), sections) }
+              _ -> Nothing
+      _ -> Nothing
+
+parseSourcesList :: String -> [DebSource]
+parseSourcesList = map parseSourceLine . sourceLines
diff --git a/testdata/merge1-expected/Debian/Repo/Sync.hs b/testdata/merge1-expected/Debian/Repo/Sync.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Sync.hs
@@ -0,0 +1,52 @@
+-- Comment above module head
+module Debian.Repo.Sync {-# WARNING "this is a warning" #-}
+    ( rsync
+    , foo
+    -- Comment after last export
+    ) where
+
+-- Comment before first import
+
+import Control.Monad.Trans (MonadIO)
+import qualified Data.ByteString as B (empty)
+import System.Exit (ExitCode)
+import System.FilePath (dropTrailingPathSeparator)
+-- Comment between two imporrts
+import System.Process (proc)
+import System.Process.Progress (keepResult, runProcessF)
+
+-- Comment before first decl
+rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode
+rsync extra source dest =
+    do result <- runProcessF (proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                                            [dropTrailingPathSeparator source ++ "/",
+                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult
+       case result of
+         [x] -> return x
+         _ -> error "Missing or multiple exit codes"
+
+-- Comment between two decls
+foo :: Int
+foo = 1
+
+{-
+handleExit 1 = "Syntax or usage error"
+handleExit 2 = "Protocol incompatibility"
+handleExit 3 = "Errors selecting input/output files, dirs"
+handleExit 4 = "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+handleExit 5 = "Error starting client-server protocol"
+handleExit 6 = "Daemon unable to append to log-file"
+handleExit 10 = "Error in socket I/O"
+handleExit 11 = "Error in file I/O"
+handleExit 12 = "Error in rsync protocol data stream"
+handleExit 13 = "Errors with program diagnostics"
+handleExit 14 = "Error in IPC code"
+handleExit 20 = "Received SIGUSR1 or SIGINT"
+handleExit 21 = "Some error returned by waitpid()"
+handleExit 22 = "Error allocating core memory buffers"
+handleExit 23 = "Partial transfer due to error"
+handleExit 24 = "Partial transfer due to vanished source files"
+handleExit 25 = "The --max-delete limit stopped deletions"
+handleExit 30 = "Timeout in data send/receive"
+handleExit 35 = "Timeout waiting for daemon connection"
+-}
diff --git a/testdata/merge1-expected/Debian/Repo/Types.hs b/testdata/merge1-expected/Debian/Repo/Types.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types.hs
@@ -0,0 +1,11 @@
+module Debian.Repo.Types
+    ( module Debian.Repo.Types.AptImage
+    , module Debian.Repo.Types.EnvPath
+    , module Debian.Repo.Types.PackageIndex
+    , module Debian.Repo.Types.Release
+    ) where
+
+import Debian.Repo.Types.AptImage
+import Debian.Repo.Types.EnvPath
+import Debian.Repo.Types.PackageIndex
+import Debian.Repo.Types.Release
diff --git a/testdata/merge1-expected/Debian/Repo/Types/AptBuildCache.hs b/testdata/merge1-expected/Debian/Repo/Types/AptBuildCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/AptBuildCache.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptBuildCache
+    ( AptBuildCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache)
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+-- | An apt cache with extra sources.list lines for a local upload repository.
+class AptCache t => AptBuildCache t where
+    -- | The sources.list
+    aptSliceList :: t -> SliceList
diff --git a/testdata/merge1-expected/Debian/Repo/Types/AptCache.hs b/testdata/merge1-expected/Debian/Repo/Types/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/AptCache.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptCache
+    ( AptCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+class (Ord t, Eq t, Show t) => AptCache t where
+    globalCacheDir :: t -> FilePath
+    -- | The directory you might chroot to.
+    rootDir :: t -> EnvRoot
+    -- | The sources.list without the local repository
+    aptBaseSliceList :: t -> SliceList
+    -- | The build architecture
+    aptArch :: t -> Arch
+    -- | Return the all source packages in this AptCache.
+    aptSourcePackages :: t -> [SourcePackage]
+    -- | Return the all binary packages for the architecture of this AptCache.
+    aptBinaryPackages :: t -> [BinaryPackage]
+    -- | Name of release
+    aptReleaseName :: t -> ReleaseName
diff --git a/testdata/merge1-expected/Debian/Repo/Types/AptImage.hs b/testdata/merge1-expected/Debian/Repo/Types/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/AptImage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptImage
+    ( AptImage(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import qualified Debian.Repo.Types.Slice as T (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+data AptImage =
+    AptImage { aptGlobalCacheDir :: FilePath
+             , aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSliceList :: T.SliceList
+             , aptImageReleaseName :: ReleaseName
+             , aptImageSourcePackages :: [SourcePackage]
+             , aptImageBinaryPackages :: [BinaryPackage]
+             }
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (aptImageReleaseName apt)
+
+instance AptCache AptImage where
+    globalCacheDir = aptGlobalCacheDir
+    rootDir = aptImageRoot
+    aptArch = aptImageArch
+    aptBaseSliceList = aptImageSliceList
+    aptSourcePackages = aptImageSourcePackages
+    aptBinaryPackages = aptImageBinaryPackages
+    aptReleaseName = aptImageReleaseName
+
+instance Ord AptImage where
+    compare a b = compare (aptImageReleaseName a) (aptImageReleaseName b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
diff --git a/testdata/merge1-expected/Debian/Repo/Types/EnvPath.hs b/testdata/merge1-expected/Debian/Repo/Types/EnvPath.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/EnvPath.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.EnvPath
+    ( EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
diff --git a/testdata/merge1-expected/Debian/Repo/Types/PackageIndex.hs b/testdata/merge1-expected/Debian/Repo/Types/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/PackageIndex.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.PackageIndex
+    ( PackageIndex(..)
+    , PackageIndexLocal
+    , PackageID(packageVersion)
+    , prettyPackageID
+    , BinaryPackage(..)
+    , binaryPackageName
+    , prettyBinaryPackage
+    , makeBinaryPackageID
+    , SourcePackage(..)
+    , sourcePackageName
+    , makeSourcePackageID
+    , SourceControl(..)
+    , SourceFileSpec(..)
+    , PackageIDLocal
+    , BinaryPackageLocal
+    , SourcePackageLocal
+    , PackageVersion(..)
+    , PkgVersion(..)
+    , prettyPkgVersion
+    ) where
+
+import Data.Text (Text, map)
+import Debian.Arch (Arch(..))
+import qualified Debian.Control.Text as T (Paragraph)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName, Relations)
+import Debian.Release (Section(..))
+import Debian.Repo.Orphans ({- instances -})
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)
+
+class (Eq a, Ord a) => PackageVersion a where
+    pkgName :: a -> BinPkgName
+    pkgVersion :: a -> DebianVersion
+
+-- |This is an old type which is still used to interface with the
+-- Debian.Relation module.
+data PkgVersion = PkgVersion { getName :: BinPkgName
+                             , getVersion :: DebianVersion
+                             } deriving (Eq, Ord, Show)
+
+instance PackageVersion PkgVersion where
+    pkgName = getName
+    pkgVersion = getVersion
+
+prettyPkgVersion :: PkgVersion -> Doc
+prettyPkgVersion v = pretty (getName v) <> text "=" <> prettyDebianVersion (getVersion v)
+
+---------------- PACKAGES AND PACKAGE INDEXES -------------
+
+-- |The PackageIndex type represents a file containing control
+-- information about debian packages, either source or binary.
+-- Though the control information for a binary package does not
+-- specify an architecture, the architecture here is that of
+-- the environment where the package information is cached.
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+type PackageIndexLocal = PackageIndex
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage p = pretty (pkgName p) <> text "-" <> prettyDebianVersion (pkgVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
+
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+
+-- | The 'PackageID' type fully identifies a package by name, version,
+-- and a 'PackageIndex' which identifies the package's release,
+-- component and architecture.
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+binaryPackageName :: BinaryPackage -> BinPkgName
+binaryPackageName = packageName . packageID
+
+sourcePackageName :: SourcePackage -> SrcPkgName
+sourcePackageName = packageName . sourcePackageID
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = pretty (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      }
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+type PackageIDLocal n = PackageID n
+type BinaryPackageLocal = BinaryPackage
+type SourcePackageLocal = SourcePackage
diff --git a/testdata/merge1-expected/Debian/Repo/Types/Release.hs b/testdata/merge1-expected/Debian/Repo/Types/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/Release.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , makeReleaseInfo
+    , parseComponents
+    , parseArchitectures
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure))
+import Data.Text (Text, unpack)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (fieldValue, Paragraph)
+import Debian.Release (parseSection', ReleaseName(..), Section(..))
+import Text.Regex (mkRegex, splitRegex)
+import qualified Tmp.File as F (File(..))
+
+-- FIXME: The lists here should be sets so that == and compare work properly.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: [Arch]
+                       , releaseComponents :: [Section]
+                       } deriving (Eq, Ord, Read, Show)
+
+makeReleaseInfo :: F.File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(F.File {F.text = Failure msgs}) _name _aliases =
+    error $ "Failure reading " ++ show (F.path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(F.File {F.text = Success info}) name aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = name
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (F.path file)
+
+parseArchitectures :: Text -> [Arch]
+parseArchitectures archList =
+    map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
diff --git a/testdata/merge1-expected/Debian/Repo/Types/Repo.hs b/testdata/merge1-expected/Debian/Repo/Types/Repo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/Repo.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repo
+    ( Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    ) where
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Text (unpack)
+import Debian.Repo.Types.EnvPath (EnvPath(..))
+import Debian.Repo.Types.Release (Release)
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
diff --git a/testdata/merge1-expected/Debian/Repo/Types/Repository.hs b/testdata/merge1-expected/Debian/Repo/Types/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/Repository.hs
@@ -0,0 +1,428 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repository
+    ( Repository
+    , LocalRepository, repoRoot, repoLayout, repoReleaseInfoLocal
+    , RemoteRepository
+    , Layout(..)
+    , MonadRepoCache(getRepoCache, putRepoCache)
+    , loadRepoCache
+    , saveRepoCache
+    , fromLocalRepository
+    , readLocalRepo
+    , prepareLocalRepository
+    , copyLocalRepo -- repoCD
+    , prepareRepository
+    , setRepositoryCompatibility
+    , flushLocalRepository
+    , poolDir'
+    -- , sliceReleaseNames
+    , repoReleaseNames
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure), maybeRead)
+import Control.Exception (ErrorCall(..), SomeException, toException)
+import Control.Monad (filterM, unless, when)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IO (catch)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.List (groupBy, intercalate, isPrefixOf, partition, sort)
+import Data.Map as Map (empty, fromList, insert, insertWith, lookup, Map, toList, union)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (Text, unpack)
+import Debian.Changes (ChangedFileSpec(changedFileSection), ChangesFile(changeInfo))
+import qualified Debian.Control.Text as T (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph, Paragraph')
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section, sectionName', SubSection(section))
+import Debian.Repo.Monads.Top (MonadTop, sub)
+import Debian.Repo.Sync (rsync)
+import Debian.Repo.Types.EnvPath (EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Types.Release (makeReleaseInfo, Release(releaseName))
+import Debian.Repo.Types.Repo (compatibilityFile, libraryCompatibilityLevel, Repo(..), RepoKey(..))
+import Debian.URI (dirFromURI, fileFromURI, fromURI', toURI', URI(uriScheme, uriPath), URI', uriToString')
+import Debian.UTF8 as Deb (decode)
+import Extra.Files (maybeWriteFile)
+import Extra.List (partitionM)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink, setFileMode)
+import System.Process.Progress (qPutStr, qPutStrLn, quieter)
+import System.Unix.Directory (removeRecursiveSafely)
+import qualified Text.Format as F (Pretty(..))
+import Text.PrettyPrint.ANSI.Leijen (text)
+import Text.Regex (matchRegex, mkRegex)
+import qualified Tmp.File as F (File(..), readFile, Source(RemotePath))
+
+--------------------- REPOSITORY -----------------------
+
+-- | The Repository type reprents any instance of the Repo class, so
+-- it might be local or remote.
+--data Repository = forall a. (Repo a) => Repository a
+data Repository
+    = LocalRepo LocalRepository
+    | RemoteRepo RemoteRepository
+    deriving (Show, Read)
+
+instance Ord Repository where
+    compare a b = compare (repoKey a) (repoKey b)
+
+instance Eq Repository where
+    a == b = compare a b == EQ
+
+-- | URI has a bogus show function, which we are using here.
+instance F.Pretty URI' where
+    pretty = text . show . fromURI'
+
+instance F.Pretty Repository where
+    pretty (LocalRepo r) = text $ outsidePath (repoRoot r)
+    pretty (RemoteRepo r) = F.pretty r
+
+instance Repo Repository where
+    repoKey (LocalRepo (LocalRepository path _ _)) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoKey (RemoteRepo (RemoteRepository uri _)) = Remote uri
+    repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
+    repoReleaseInfo (RemoteRepo (RemoteRepository _ info)) = info
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot_ :: EnvPath
+      , repoLayout_ :: (Maybe Layout)
+      , repoReleaseInfoLocal_ :: [Release]
+      } deriving (Read, Show)
+
+instance Ord LocalRepository where
+    compare (LocalRepository a _ _) (LocalRepository b _ _) = compare a b
+
+instance Eq LocalRepository where
+    a == b = compare a b == EQ
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show)
+
+instance Ord RemoteRepository where
+    compare (RemoteRepository a _) (RemoteRepository b _) = compare a b
+
+instance Eq RemoteRepository where
+    a == b = compare a b == EQ
+
+instance F.Pretty RemoteRepository where
+    pretty (RemoteRepository s _) = F.pretty s
+
+repoRoot :: LocalRepository -> EnvPath
+repoRoot = repoRoot_
+
+repoLayout :: LocalRepository -> Maybe Layout
+repoLayout = repoLayout_
+
+repoReleaseInfoLocal :: LocalRepository -> [Release]
+repoReleaseInfoLocal = repoReleaseInfoLocal_
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+class MonadIO m => MonadRepoCache m where
+    getRepoCache :: m (Map RepoKey Repository)
+    putRepoCache :: Map RepoKey Repository -> m ()
+
+modifyRepoCache :: MonadRepoCache m => (Map RepoKey Repository -> Map RepoKey Repository) -> m ()
+modifyRepoCache f = do
+    s <- getRepoCache
+    putRepoCache (f s)
+
+fromLocalRepository :: LocalRepository -> Repository
+fromLocalRepository = LocalRepo
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: (MonadRepoCache m, MonadTop m) => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty)) >>= putRepoCache
+    where
+      loadRepoCache' :: FilePath -> IO (Map RepoKey Repository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: (MonadIO m, MonadTop m, MonadRepoCache m) => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- getRepoCache
+             repoCache <- liftIO $ loadCache path
+             let merged = show . Map.toList $ Map.union live repoCache
+             liftIO (F.removeLink path `IO.catch` (\e -> unless (isDoesNotExistError e) (ioError e))) >> liftIO (writeFile path merged)
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map RepoKey Repository)
+            loadCache path =
+                readFile path `IO.catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+readLocalRepo :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+readLocalRepo root layout =
+    do state <- getRepoCache
+       case Map.lookup (Local root) state of
+         Just (RemoteRepo (RemoteRepository _ _)) -> error "readLocalRepo: internal error" -- somehow a remote repo got associated with a Local RepoKey
+         Just (LocalRepo repo) -> return repo
+         Nothing ->
+             do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+                (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+                linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+                let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+                let distGroups = groupBy fstEq . sort $ aliasPairs
+                let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+                releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+                qPutStrLn ("LocalRepository releaseInfo " ++ show root ++ ": " ++ show releaseInfo)
+                let repo = LocalRepository { repoRoot_ = root
+                                           , repoLayout_ = layout
+                                           , repoReleaseInfoLocal_ = releaseInfo }
+                putRepoCache (insertWith (\ _ x -> x) (Local root) (LocalRepo repo) state)
+                return repo
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist ++ "/Release"
+      distDir = outsidePath root ++ "/dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path dist aliases =
+    liftIO (F.readFile path) >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> F.File Text -> Release
+parseRelease name aliases file =
+    case F.text file of
+      Failure msgs -> error $ "Could not read " ++ show (F.path file) ++ ": " ++ show msgs
+      Success text ->
+          case T.parseControl (show (F.path file)) text of
+            Left msg -> error $ "Failure parsing " ++ show (F.path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (F.path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (F.File {F.path = F.path file, F.text = Success info}) name aliases
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository root layout =
+    do mapM_ (liftIO . initDir)
+                 [(".", 0o40755),
+                  ("dists", 0o40755),
+                  ("incoming", 0o41755),
+                  ("removed", 0o40750),
+                  ("reject", 0o40750)]
+       layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+                  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+       mapM_ (liftIO . initDir)
+                 (case layout' of
+                    Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+                    Just Flat -> []
+                    Nothing -> [])
+       readLocalRepo root layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+{-      notSymbolicLink root name =
+          getSymbolicLinkStatus (root ++ "/dists/" ++ name) >>= return . not . isSymbolicLink
+      hasReleaseFile root name =
+          doesFileExist (root ++ "/dists/" ++ name ++ "/Release") -}
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadRepoCache m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do qPutStrLn ("Syncing local repository from " ++ src ++ " -> " ++ dst)
+       liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       result <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         ExitSuccess ->
+             do let repo' = repo {repoRoot_ = dest}
+                modifyRepoCache (Map.insert (Local dest) (LocalRepo repo'))
+                return repo'
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path text
+    where text = show libraryCompatibilityLevel ++ "\n"
+          path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepoCache m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       prepareLocalRepository (repoRoot r) (repoLayout r)
+
+prepareRepository :: MonadRepoCache m => RepoKey -> m Repository
+prepareRepository key =
+    case key of
+      Local path -> prepareLocalRepository path Nothing >>= return . LocalRepo
+      Remote uri -> prepareRepository' (fromURI' uri)
+
+-- | Prepare a repository, which may be remote or local depending on
+-- the URI.
+prepareRepository' :: MonadRepoCache m => URI -> m Repository
+prepareRepository' uri =
+    do state <- getRepoCache
+       repo <- maybe newRepo return (Map.lookup (Remote (toURI' uri)) state)
+       putRepoCache (Map.insert (Remote (toURI' uri)) repo state)
+       return repo
+    where
+      newRepo =
+             case uriScheme uri of
+               "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= return . LocalRepo
+               -- FIXME: We only want to verifyRepository on demand.
+               _ -> verifyRepository (toURI' uri)
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+{-# NOINLINE verifyRepository #-}
+verifyRepository :: MonadRepoCache m => URI' -> m Repository
+verifyRepository uri =
+    do state <- getRepoCache
+       case Map.lookup (Remote uri) state of
+         Just repo -> return repo
+         Nothing ->
+             do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+                let repo = RemoteRepo (RemoteRepository uri releaseInfo)
+                modifyRepoCache (Map.insert (Remote uri) repo)
+                return repo
+
+-- Nice code to do caching, but I figured out how to fix the old code.
+
+--instance Read URI where
+--    readsPrec _ s = [(fromJust (parseURI s), "")]
+--
+---- |Get the list of releases of a remote repository.
+--getReleaseInfo :: FilePath
+--               -> Bool     -- ^ If False don't look at existing cache
+--               -> URI
+--               -> IO [ReleaseInfo]
+--getReleaseInfo top tryCache uri =
+--    readCache >>= \ cache ->
+--    return (lookup uri cache) >>=
+--    maybe (updateCache cache) return
+--    where
+--      cachePath = top ++ "/repoCache"
+--      readCache :: IO [(URI, [ReleaseInfo])]
+--      readCache = if tryCache
+--                  then try (readFile cachePath >>= return . read) >>= return . either (\ (_ :: SomeException) -> []) id
+--                  else return []
+--      updateCache :: [(URI, [ReleaseInfo])] -> IO [ReleaseInfo]
+--      updateCache pairs = getReleaseInfoRemote uri >>= \ info ->
+--                          writeCache ((uri, info) : pairs) >> return info
+--      writeCache :: [(URI, [ReleaseInfo])] -> IO ()
+--      writeCache pairs = writeFile (show pairs) cachePath
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    quieter 2 (dirFromURI distsURI) >>=
+    quieter 2 . either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [F.File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (F.File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: F.File (T.Paragraph' Text) -> Maybe Text
+      getSuite (F.File {F.text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (F.File {F.text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (F.File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ F.File {F.path = F.RemotePath releaseURI, F.text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
+
+repoReleaseNames :: Repository -> [ReleaseName]
+repoReleaseNames (RemoteRepo (RemoteRepository _ rels)) = map releaseName rels
+repoReleaseNames (LocalRepo (LocalRepository _ _ rels)) = map releaseName rels
diff --git a/testdata/merge1-expected/Debian/Repo/Types/Slice.hs b/testdata/merge1-expected/Debian/Repo/Types/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Debian/Repo/Types/Slice.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Slice
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    ) where
+
+import qualified Debian.Repo.Types.Repo as R (RepoKey)
+import Debian.Sources (DebSource(..), SliceName(..), SourceType(..))
+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), vcat)
+
+data Slice = Slice {sliceRepoKey :: R.RepoKey, sliceSource :: DebSource} deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty SliceList where
+    pretty = vcat . map (pretty . sliceSource) . slices
+
+deriving instance Show SourceType
+deriving instance Show DebSource
diff --git a/testdata/merge1-expected/Text/Format.hs b/testdata/merge1-expected/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/merge1-expected/Tmp/File.hs b/testdata/merge1-expected/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge1-expected/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
diff --git a/testdata/merge2-expected/Debian/Repo/AptCache.hs b/testdata/merge2-expected/Debian/Repo/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/AptCache.hs
@@ -0,0 +1,393 @@
+{- This is a comment at the top -}   
+{-# LANGUAGE DeriveDataTypeable, PackageImports #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |An AptCache represents a local cache of a remote repository.  The
+-- cached information is usually downloaded by running "apt-get
+-- update", and appears in @\/var\/lib\/apt\/lists@.
+module Debian.Repo.AptCache {-# WARNING "this is a warning" #-}
+    ( SourcesChangedAction(..)
+    , aptSourcePackagesSorted
+    , sliceIndexes
+    , cacheDistDir
+    , distDir
+    , aptDir
+    , cacheRootDir
+    , cacheSourcesPath
+    , sourcesPath
+    , sourceDir
+    , aptCacheFiles
+    , aptCacheFilesOfSlice
+    , archFiles
+    , buildArchOfEnv
+    , buildArchOfRoot
+    , updateCacheSources
+    , sourcePackages
+    , binaryPackages
+    , runAptGet
+    , aptOpts
+    , getSourcePackagesBase
+    , getBinaryPackagesBase
+    , getSourcePackagesBuild
+    , getBinaryPackagesBuild
+    ) where
+
+import Control.DeepSeq (force, NFData)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Data (Data)
+import Data.List (intercalate, sortBy)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Relation (BinPkgName, PkgName, SrcPkgName(..))
+import Debian.Release (ReleaseName(relName), releaseName', sectionName')
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.Package (binaryPackagesOfCachedIndex, sourcePackagesOfCachedIndex)
+import Debian.Repo.Slice (binarySlices, sourceSlices, verifySourcesList)
+import Debian.Repo.SourcesList (parseSourcesList)
+import Debian.Repo.Types.AptBuildCache (AptBuildCache(aptSliceList))
+import Debian.Repo.Types.AptCache (AptCache(..), AptCache(aptArch, aptBaseSliceList, aptBinaryPackages, aptReleaseName, aptSourcePackages, globalCacheDir))
+import Debian.Repo.Types.Common (EnvRoot(..), EnvRoot(EnvRoot))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID), binaryPackageName, PackageID(packageVersion), PackageIndex(..), SourcePackage(sourcePackageID), sourcePackageName)
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Common
+       (Repo(repoReleaseInfo), repoKey, RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache, prepareRepository)
+import Debian.Repo.Types.Common (Slice(..), SliceList(slices))
+import Debian.Sources (DebSource(..), SourceType(..))
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Extra.Files (replaceFile)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetLine, stdin)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Progress (ePutStr, ePutStrLn, qPutStrLn, runProcessF)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+instance NFData ExitCode
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+-- | A directory which will hold all the cached files for this
+-- NamedSliceList.
+cacheDistDir :: FilePath -> ReleaseName -> FilePath
+cacheDistDir cacheDir release = cacheDir ++ "/dists/" ++ relName release
+
+cacheRootDir :: FilePath -> ReleaseName -> EnvRoot
+cacheRootDir cacheDir release = EnvRoot (cacheDistDir cacheDir release ++ "/aptEnv")
+
+distDir :: AptCache c => c -> FilePath
+distDir cache = cacheDistDir (globalCacheDir cache) (aptReleaseName cache)
+
+aptDir :: AptCache c => c -> SrcPkgName -> FilePath
+aptDir cache package = distDir cache ++ "/apt/" ++ unSrcPkgName package
+
+-- | The path where a text of the SliceList is stored.
+cacheSourcesPath :: FilePath -> ReleaseName -> FilePath
+cacheSourcesPath cacheDir release = cacheDistDir cacheDir release </> "sources"
+
+sourcesPath :: AptCache c => c -> FilePath
+sourcesPath cache = cacheSourcesPath (globalCacheDir cache) (aptReleaseName cache)
+
+-- Additional functions which can only be used on already constructed
+-- instances of AptCache.
+
+-- | A directory holding all files downloaded by apt-get source for a
+-- certain package
+sourceDir :: AptCache t => t -> String -> FilePath
+sourceDir c package = distDir c ++ "/apt/" ++ package
+
+-- |Return all the named source packages sorted by version
+aptSourcePackagesSorted :: AptCache t => t -> [SrcPkgName] -> [SourcePackage]
+aptSourcePackagesSorted os names =
+    sortBy cmp . filterNames names . aptSourcePackages $ os
+    where
+      filterNames names' packages =
+          filter (flip elem names' . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: (MonadRepoCache m, AptCache a) => a -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes cache slice =
+    prepareRepository (sliceRepoKey slice) >>= \ repo ->
+    case (sourceDist (sliceSource slice)) of
+      Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+      Right (release, sections) -> return $ map (makeIndex repo release) sections
+    where
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> aptArch cache })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+-- |Return the paths in the local cache of the index files of a slice list.
+aptCacheFiles :: AptCache a => a -> [DebSource] -> [FilePath]
+aptCacheFiles apt sources = concat . map (aptCacheFilesOfSlice apt) $ sources
+
+-- |Return the paths in the local cache of the index files of a single slice.
+aptCacheFilesOfSlice :: AptCache a => a -> DebSource -> [FilePath]
+aptCacheFilesOfSlice apt slice = archFiles (aptArch apt) slice
+
+-- |Return the list of files that apt-get update would write into
+-- \/var\/lib\/apt\/lists when it processed the given list of DebSource.
+archFiles :: Arch -> DebSource -> [FilePath]
+archFiles arch deb =
+    case (arch, deb) of
+      (Binary _ _, DebSource DebSrc _ _) ->
+          map (++ "_source_Sources") (archFiles' deb)
+      (Binary _os _cpu, DebSource Deb _ _) ->
+          map (++ ("_binary-" ++ show (prettyArch arch) ++ "_Packages")) (archFiles' deb)
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+archFiles' :: DebSource -> [FilePath]
+archFiles' deb =
+    let uri = sourceUri deb
+        distro = sourceDist deb in
+    let scheme = uriScheme uri
+        auth = uriAuthority uri
+        path = uriPath uri in
+    let userpass = maybe "" uriUserInfo auth
+        reg = maybeOfString $ maybe "" uriRegName auth
+        port = maybe "" uriPort auth in
+    let (user, pass) = break (== ':') userpass in
+    let user' = maybeOfString user
+        pass' = maybeOfString pass in
+    let uriText = prefix scheme user' pass' reg port path in
+    -- what about dist?
+    either (\ exact -> [(escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText ++ escape exact))])
+           (\ (dist, sections) ->
+                map (\ section ->
+                         (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+                          releaseName' dist ++ "_" ++ sectionName' section))
+                    sections)
+           distro
+    where
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid DebSource: " ++ show (pretty deb))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+
+buildArchOfEnv :: EnvRoot -> IO Arch
+buildArchOfEnv (EnvRoot root)  =
+    do setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+       a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (ArchOS os) (ArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+{-
+  (err, _) <- useEnv root forceList (readProcessChunks (shell cmd) L.empty) >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "export LOGNAME=root; dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+{-
+    do (code, out, err, _) <- runProcess (shell cmd) L.empty >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s =
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.
+updateCacheSources :: (MonadApt m, AptCache c) => SourcesChangedAction -> c -> m c
+updateCacheSources sourcesChangedAction distro =
+    -- (\ x -> qPutStrLn "Updating cache sources" >> quieter 2 x) $
+    qPutStrLn "Updating cache sources" >>
+    do
+      let baseSources = aptBaseSliceList distro
+      --let distro@(ReleaseCache _ dist _) = releaseFromConfig' top text
+      let dir = Debian.Repo.AptCache.distDir distro
+      distExists <- liftIO $ doesFileExist (Debian.Repo.AptCache.sourcesPath distro)
+      case distExists of
+        True ->
+            do
+              fileSources <- liftIO (readFile (Debian.Repo.AptCache.sourcesPath distro)) >>= verifySourcesList Nothing . parseSourcesList
+              case (fileSources == baseSources, sourcesChangedAction) of
+                (True, _) -> return ()
+                (False, SourcesChangedError) ->
+                    do
+                      ePutStrLn ("The sources.list in the existing '" ++ relName (aptReleaseName distro) ++
+                                 "' build environment doesn't match the parameters passed to the autobuilder" ++
+                                 ":\n\n" ++ Debian.Repo.AptCache.sourcesPath distro ++ ":\n\n" ++
+                                 show (pretty fileSources) ++
+                                 "\nRun-time parameters:\n\n" ++
+                                 show (pretty baseSources) ++ "\n" ++
+                                 "It is likely that the build environment in\n" ++
+                                 dir ++ " is invalid and should be rebuilt.")
+                      ePutStr $ "Remove it and continue (or exit)?  [y/n]: "
+                      result <- liftIO $ hGetLine stdin
+                      case result of
+                        ('y' : _) ->
+                            do
+                              liftIO $ removeRecursiveSafely dir
+                              liftIO $ createDirectoryIfMissing True dir
+                              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                        _ ->
+                            error ("Please remove " ++ dir ++ " and restart.")
+                (False, RemoveRelease) ->
+                    do
+                      ePutStrLn $ "Removing suspect environment: " ++ dir
+                      liftIO $ removeRecursiveSafely dir
+                      liftIO $ createDirectoryIfMissing True dir
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                (False, UpdateSources) ->
+                    do
+                      -- The sources.list has changed, but it should be
+                      -- safe to update it.
+                      ePutStrLn $ "Updating environment with new sources.list: " ++ dir
+                      liftIO $ removeFile (Debian.Repo.AptCache.sourcesPath distro)
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+        False ->
+            do
+              liftIO $ createDirectoryIfMissing True dir
+              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+      return distro
+
+-- | Return a sorted list of available source packages, newest version first.
+sourcePackages :: AptCache a => a -> [SrcPkgName] -> [SourcePackage]
+sourcePackages os names =
+    sortBy cmp . filterNames . aptSourcePackages $ os
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          filter (flip elem names . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+binaryPackages :: AptCache a => a -> [BinPkgName] -> [BinaryPackage]
+binaryPackages os names =
+    sortBy cmp . filterNames . aptBinaryPackages $ os
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          filter (flip elem names . binaryPackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
+
+getSourcePackagesBase :: (AptCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getSourcePackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBase :: (AptCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+-- | Note that apt-get source works for binary or source package names.
+runAptGet :: (PkgName n, AptCache t) => t -> FilePath -> String -> [(n, Maybe DebianVersion)] -> IO ()
+runAptGet os dir command packages =
+    createDirectoryIfMissing True dir >> runProcessF (shell cmd) L.empty >> return ()
+    where
+      cmd = (intercalate " " ("cd" : dir : "&&" : "apt-get" : aptOpts os : command : map formatPackage packages))
+      formatPackage (name, Nothing) = show (pretty name)
+      formatPackage (name, Just version) = show (pretty name) ++ "=" ++ show (prettyDebianVersion version)
+
+aptOpts :: AptCache t => t -> String
+aptOpts os =
+    (" -o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status" ++
+     " -o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists" ++
+     " -o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives" ++
+     " -o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list" ++
+     " -o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d")
+    where root = rootPath . rootDir $ os
diff --git a/testdata/merge2-expected/Debian/Repo/AptImage.hs b/testdata/merge2-expected/Debian/Repo/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/AptImage.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |The AptImage object represents a partial OS image which is capable
+-- of running apt-get, and thus obtaining repository info and source
+-- code packages.
+module Debian.Repo.AptImage
+    ( prepareAptEnv
+    , updateAptEnv
+    ) where
+
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Function (on)
+import Data.List (sortBy)
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.AptCache (aptOpts, buildArchOfRoot, cacheRootDir, getBinaryPackagesBase, getSourcePackagesBase, SourcesChangedAction, updateCacheSources)
+import Debian.Repo.Monads.Apt (insertAptImage, lookupAptImage, MonadApt(getApt, putApt))
+import Debian.Repo.Types.AptImage (AptImage(..))
+import Debian.Repo.Types.Common (EnvRoot(..))
+import Debian.Repo.Types.PackageIndex (PackageID(packageVersion), SourcePackage(sourcePackageID))
+import Debian.Repo.Types.Common
+       (NamedSliceList(sliceList, sliceListName))
+import Debian.Sources (SliceName(sliceName))
+import Extra.Files (replaceFile, writeFileIfMissing)
+import System.Directory (createDirectoryIfMissing)
+import System.Process (shell)
+import System.Process.Progress (qPutStrLn, quieter, runProcessF)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+prepareAptEnv :: MonadApt m =>
+                 FilePath               -- Put environment in a subdirectory of this
+              -> SourcesChangedAction   -- What to do if environment already exists and sources.list is different
+              -> NamedSliceList         -- The sources.list
+              -> m AptImage             -- The resulting environment
+prepareAptEnv cacheDir sourcesChangedAction sources =
+    (\ x -> qPutStrLn ("Preparing apt-get environment for " ++ show (sliceName (sliceListName sources))) >> quieter 2 x) $
+    getApt >>= return . lookupAptImage (sliceListName sources) >>=
+    maybe (prepareAptEnv' cacheDir sourcesChangedAction sources) return
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+{-# NOINLINE prepareAptEnv' #-}
+prepareAptEnv' :: MonadApt m => FilePath -> SourcesChangedAction -> NamedSliceList -> m AptImage
+prepareAptEnv' cacheDir sourcesChangedAction sources =
+    do let root = rootPath (cacheRootDir cacheDir (ReleaseName (sliceName (sliceListName sources))))
+       --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/cache/apt/archives/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/dpkg")
+       liftIO $ createDirectoryIfMissing True (root ++ "/etc/apt")
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/status") ""
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/diversions") ""
+       -- We need to create the local pool before updating so the
+       -- sources.list will be valid.
+       let sourceListText = show (pretty (sliceList sources))
+       -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+       liftIO $ replaceFile (root ++ "/etc/apt/sources.list") sourceListText
+       arch <- liftIO $ buildArchOfRoot
+       let os = AptImage { aptGlobalCacheDir = cacheDir
+                         , aptImageRoot = EnvRoot root
+                         , aptImageArch = arch
+                         , aptImageReleaseName = ReleaseName . sliceName . sliceListName $ sources
+                         , aptImageSliceList = sliceList sources
+                         , aptImageSourcePackages = []
+                         , aptImageBinaryPackages = [] }
+       os' <- updateCacheSources sourcesChangedAction os >>= updateAptEnv
+       getApt >>= putApt . insertAptImage (sliceListName sources) os'
+       return os'
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+{-# NOINLINE updateAptEnv #-}
+updateAptEnv :: MonadApt m => AptImage -> m AptImage
+updateAptEnv os =
+    liftIO (runProcessF (shell cmd) L.empty) >>
+    getSourcePackagesBase os >>= return . sortBy cmp >>= \ sourcePackages ->
+    getBinaryPackagesBase os >>= \ binaryPackages ->
+    return $ os { aptImageSourcePackages = sourcePackages
+                , aptImageBinaryPackages = binaryPackages }
+    where
+      cmd = "apt-get" ++ aptOpts os ++ " update"
+      -- Flip args to get newest version first
+      cmp = flip (compare `on` (packageVersion . sourcePackageID))
+{-
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+    putStrLn ("> " ++ cmd) >> system cmd >>= \ code ->
+    case code of
+      ExitSuccess -> return ()
+      ExitFailure n -> error $ cmd ++ " -> ExitFailure " ++ show n
+-}
diff --git a/testdata/merge2-expected/Debian/Repo/Monads/Apt.hs b/testdata/merge2-expected/Debian/Repo/Monads/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Monads/Apt.hs
@@ -0,0 +1,195 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Debian.Repo.Monads.Apt
+    ( MonadApt(..)
+    , AptIOT
+    , AptIO
+    -- * AptIO Monad
+    , runAptIO
+    , runAptT
+    , tryAB
+    , tryJustAB
+    -- * State
+    , AptState
+    , initState
+    , setRepoMap
+    , getRepoMap
+    , lookupRepository
+    , insertRepository
+    , lookupAptImage
+    , insertAptImage
+    , lookupSourcePackages
+    , insertSourcePackages
+    , lookupBinaryPackages
+    , insertBinaryPackages
+    , readParagraphs
+    , findRelease
+    , putRelease
+    , countTasks
+    ) where
+
+import Control.Exception (try)
+import Control.Exception as E (Exception, tryJust)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, mapStateT, MonadIO(..), MonadTrans(..), put, StateT(runStateT))
+import qualified Data.Map as Map (empty, insert, lookup, Map)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(parseControlFromHandle), Paragraph)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Common (Repo(repoKey), RepoKey(..))
+import Debian.Repo.Types.Repository (MonadRepoCache(getRepoCache, putRepoCache), Repository)
+import Debian.Sources (SliceName)
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+import System.Process.Progress (ePutStrLn)
+import Text.Printf (printf)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+type AptIO = AptIOT IO
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository           -- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage       -- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage]) -- ^ The contents of a package index, and status of local cache of index file
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+-- |Perform an AptIO monad task in the IO monad.
+runAptIO :: AptIO a -> IO a
+runAptIO action = (runStateT action) initState >>= \ (a, _) -> return a
+
+runAptT :: Monad m => AptIOT m a -> m a
+runAptT action = (runStateT action) initState >>= return . fst
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryAB :: Exception e => AptIO a -> AptIO (Either e a)
+tryAB task =
+    do state <- get
+       mapStateT (try' state) task
+    where
+      try' state task' =
+          do result <- try task'
+             case result of
+               Left e -> return (Left e, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryJustAB :: Exception e => (e -> Maybe b) -> AptIO a -> AptIO (Either b a)
+tryJustAB f task =
+    do state <- get
+       mapStateT (tryJust' state) task
+    where
+      tryJust' state task' =
+          do result <- tryJust f task'
+             case result of
+               Left b -> return (Left b, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+setRepoMap :: Map.Map RepoKey Repository -> AptState -> AptState
+setRepoMap m state = state {repoMap = m}
+
+getRepoMap :: AptState -> Map.Map RepoKey Repository
+getRepoMap state = repoMap state
+
+lookupRepository :: RepoKey -> AptState -> Maybe Repository
+lookupRepository uri state = Map.lookup uri (repoMap state)
+
+insertRepository :: RepoKey -> Repository -> AptState -> AptState
+insertRepository uri repo state = state {repoMap = Map.insert uri repo (repoMap state)}
+
+lookupAptImage :: SliceName -> AptState -> Maybe AptImage
+lookupAptImage name state = Map.lookup  name (aptImageMap state)
+
+insertAptImage :: SliceName -> AptImage -> AptState -> AptState
+insertAptImage name image state = state {aptImageMap = Map.insert name image (aptImageMap state)}
+
+lookupSourcePackages :: FilePath -> AptState -> Maybe (FileStatus, [SourcePackage])
+lookupSourcePackages key state = Map.lookup key (sourcePackageMap state)
+
+insertSourcePackages :: FilePath -> (FileStatus, [SourcePackage]) -> AptState -> AptState
+insertSourcePackages key packages state = state {sourcePackageMap = Map.insert key packages (sourcePackageMap state)}
+
+lookupBinaryPackages :: FilePath -> AptState -> Maybe (FileStatus, [BinaryPackage])
+lookupBinaryPackages key state = Map.lookup key (binaryPackageMap state)
+
+insertBinaryPackages :: FilePath -> (FileStatus, [BinaryPackage]) -> AptState -> AptState
+insertBinaryPackages key packages state =
+    state {binaryPackageMap = Map.insert key packages (binaryPackageMap state)}
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)                 -- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")     -- Debugging output
+       return paragraphs
+
+findRelease :: Repository -> ReleaseName -> AptState -> Maybe Release
+findRelease repo dist state =
+    Map.lookup (repoKey repo, dist) (releaseMap state)
+
+putRelease :: Repository -> ReleaseName -> Release -> AptState -> AptState
+putRelease repo dist release state =
+    state {releaseMap = Map.insert (repoKey repo, dist) release (releaseMap state)}
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/merge2-expected/Debian/Repo/Monads/Top.hs b/testdata/merge2-expected/Debian/Repo/Monads/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Monads/Top.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Monads.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    ) where
+
+import Control.Monad.Reader (MonadReader(ask), ReaderT(runReaderT))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class Monad m => MonadTop m where
+    askTop :: m FilePath
+
+instance Monad m => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
diff --git a/testdata/merge2-expected/Debian/Repo/Orphans.hs b/testdata/merge2-expected/Debian/Repo/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Orphans.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Orphans where
+
+import Data.Text (Text)
+import qualified Debian.Control.Text as T
+
+deriving instance Show (T.Field' Text)
+deriving instance Ord (T.Field' Text)
+deriving instance Show T.Paragraph
+deriving instance Ord T.Paragraph
diff --git a/testdata/merge2-expected/Debian/Repo/Package.hs b/testdata/merge2-expected/Debian/Repo/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Package.hs
@@ -0,0 +1,367 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package
+    ( sourceFilePaths
+    , binaryPackageSourceVersion
+    , binarySourceVersion
+    , sourcePackageBinaryNames
+    , sourceBinaryNames
+    , toSourcePackage
+    , toBinaryPackage
+    , binaryPackageSourceID
+    , sourcePackageBinaryIDs
+    , sourcePackagesOfIndex
+    , sourcePackagesOfCachedIndex
+    , binaryPackagesOfIndex
+    , binaryPackagesOfCachedIndex
+    , getPackages
+    , putPackages
+    , releaseSourcePackages
+    , releaseBinaryPackages
+    , TH.FixityDirection(..)
+    , TH.Fixity(..)
+    ) where
+
+import Control.Exception as E (catch, ErrorCall(..), SomeException(..), try)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString, fromChunks)
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, intersperse, map, partition)
+import Data.Maybe (catMaybes)
+import Data.Set as Set (fromList, map, Set, unions)
+import qualified Data.Text as T (concat, pack, Text, unpack)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Control (ControlFunctions(asString, stripWS), formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(lookupP), Field, Field'(Field), fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Monads.Apt (insertBinaryPackages, insertSourcePackages, lookupBinaryPackages, lookupSourcePackages, MonadApt(getApt, putApt), readParagraphs)
+import Debian.Repo.PackageIndex (binaryIndexList, packageIndexPath, sourceIndexList)
+import Debian.Repo.Types.AptCache (AptCache(aptArch, rootDir))
+import Debian.Repo.Types.Common (EnvRoot(rootPath), outsidePath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(..), BinaryPackageLocal, binaryPackageName, makeBinaryPackageID, makeSourcePackageID, PackageID(..), PackageIndex(..), PackageIndexLocal, SourceControl(..), SourceFileSpec(SourceFileSpec, sourceFileName), SourcePackage(..))
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Common (RepoKey, repoKeyURI)
+import Debian.Repo.Types.Repository (LocalRepository, MonadRepoCache, repoRoot)
+import Debian.URI (fileFromURIStrict)
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Extra.Files (writeAndZipFileWithBackup)
+import qualified Language.Haskell.TH.Syntax as TH (FixityDirection(..), Fixity(..))
+import Network.URI (escapeURIString, URI(..), URIAuth(..), uriToString)
+import System.FilePath ((</>), takeDirectory)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+useConstants :: Constants p => p -> Maybe Bool
+useConstants x = asBool x
+
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
+
+sourceFilePaths :: SourcePackage -> Set FilePath
+sourceFilePaths package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = binaryPackageName package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
+
+-- |Return the name and version number of the source package that
+-- generated this binary package.
+-- see also: 'binaryPackageSourceVersion'
+binarySourceVersion :: Paragraph' T.Text -> Maybe ((BinPkgName, DebianVersion), (String, DebianVersion))
+binarySourceVersion paragraph =
+    let mBinaryName = fmap (BinPkgName . T.unpack) $ B.fieldValue "Package" paragraph
+        mBinaryVersion = fmap (parseDebianVersion . T.unpack) $ B.fieldValue "Version" paragraph
+    in
+      case (mBinaryName, mBinaryVersion) of
+        (Just binaryName, Just binaryVersion) ->
+            fmap ((,) (binaryName, binaryVersion)) $ binarySourceVersion' binaryName binaryVersion paragraph
+        _ -> Nothing
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
+
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
+
+sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+sourceBinaryNames paragraph =
+    case B.fieldValue "Binary" paragraph of
+      Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID (PackageIndex component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+
+sourcePackageBinaryIDs :: Arch -> PackageIndex -> SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs Source _ _ = error "invalid argument"
+sourcePackageBinaryIDs arch sourceIndex package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfCachedIndex :: (AptCache a, MonadApt m) => a -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupSourcePackages path state
+       status <- liftIO $ getFileStatus path `E.catch` (\ (_ :: IOError) -> error $ "Sources.list seems out of sync.  If a new release has been created you probably need to remove " ++ takeDirectory (rootPath (rootDir cache)) ++ " and try again - sorry about that.")
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toSourcePackage index) paragraphs
+                 putApt (insertSourcePackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfCachedIndex :: (MonadApt m, AptCache a) => a -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupBinaryPackages path state
+       status <- liftIO $ getFileStatus path
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toBinaryPackage release index) paragraphs
+                 putApt (insertBinaryPackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+-- | Return a list of all source packages.
+releaseSourcePackages :: MonadRepoCache m => RepoKey -> Release -> m (Set SourcePackage)
+releaseSourcePackages repo release =
+    mapM (sourcePackagesOfIndex repo release) (sourceIndexList release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages :: MonadRepoCache m => RepoKey -> Release -> m (Set BinaryPackage)
+releaseBinaryPackages repo release =
+    mapM (binaryPackagesOfIndex repo release) (binaryIndexList release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Write a set of packages into a package index.
+putPackages :: LocalRepository -> Release -> PackageIndexLocal ->  [BinaryPackageLocal] -> IO ()
+putPackages repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
diff --git a/testdata/merge2-expected/Debian/Repo/PackageIndex.hs b/testdata/merge2-expected/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,87 @@
+module Debian.Repo.PackageIndex
+    ( packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPathList
+    , packageIndexDirList
+    , packageIndexList
+    , sourceIndexList
+    , binaryIndexList
+    , releaseDir
+    , showIndexBrief
+    , debSourceFromIndex
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Types (PackageIndex(..), Release(..))
+import Debian.Repo.Types.Common (repoURI)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (DebSource(..), SourceType(..))
+import System.FilePath ((</>))
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index ++ "/" ++ packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release ++ "/" ++ sectionName' (packageIndexComponent index) ++ "/source"
+      _ -> (releaseDir release ++ "/" ++
+            sectionName' (packageIndexComponent index) ++
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "/binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists/" ++ (releaseName' . releaseName $ release)
+
+packageIndexPathList :: Release -> [FilePath]
+packageIndexPathList release = map (packageIndexPath release) . packageIndexList $ release
+
+packageIndexDirList :: Release -> [FilePath]
+packageIndexDirList release = map (packageIndexDir release) . packageIndexList $ release
+
+packageIndexList :: Release -> [PackageIndex]
+packageIndexList release = sourceIndexList release ++ binaryIndexList release
+
+sourceIndexList :: Release -> [PackageIndex]
+sourceIndexList release =
+    map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexList :: Release -> [PackageIndex]
+binaryIndexList release =
+    concat . map componentIndexes $ (releaseComponents release)
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          map archIndex (filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+showIndexBrief :: (Repository, Release) -> PackageIndex -> String
+showIndexBrief release index =
+    (releaseName' . releaseName . snd $ release) </> sectionName' (packageIndexComponent index) </> showArch (packageIndexArch index)
+    where showArch Source = "source"
+          showArch All = "all"
+          showArch x@(Binary _ _) = "binary-" ++ show (prettyArch x)
+
+debSourceFromIndex :: (Repository, Release) -> PackageIndex -> DebSource
+debSourceFromIndex (repo, release) index =
+    DebSource {sourceType = typ,
+               sourceUri = repoURI repo,
+               sourceDist = Right (dist, components)}
+    where
+      typ = case arch of (Binary _ _) -> Deb; Source -> DebSrc; All -> Deb
+      arch = packageIndexArch index
+      dist = releaseName $ release
+      components = releaseComponents $ release
diff --git a/testdata/merge2-expected/Debian/Repo/Slice.hs b/testdata/merge2-expected/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Slice.hs
@@ -0,0 +1,132 @@
+{-# LANGUAGE PackageImports, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , verifySourceLine
+    , verifySourcesList
+    , repoSources
+    , parseNamedSliceList
+    , parseNamedSliceList'
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName)
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.SourcesList (parseSourceLine, parseSourcesList)
+import Debian.Repo.Types (EnvPath(..), EnvRoot(..))
+import Debian.Repo.Types.Common (repoKey, RepoKey(..))
+import Debian.Repo.Types.Repository (prepareRepository)
+import Debian.Repo.Types.Common
+       (NamedSliceList(..), Slice(..), SliceList(..))
+import Debian.Sources (DebSource(..), SliceName(SliceName), SourceType(..))
+import Debian.URI (dirFromURI, fileFromURI, toURI')
+import Network.URI (URI(uriScheme, uriPath))
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+-- |Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadApt m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+parseNamedSliceList :: MonadApt m => (String, String) -> m (Maybe NamedSliceList)
+parseNamedSliceList (name, text) =
+    (verifySourcesList Nothing . parseSourcesList) text >>=
+    \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+-- |Create ReleaseCache info from an entry in the config file, which
+-- includes a dist name and the lines of the sources.list file.
+-- This also creates the basic 
+parseNamedSliceList' :: MonadApt m => (String, String) -> m NamedSliceList
+parseNamedSliceList' (name, text) =
+    do sources <- (verifySourcesList Nothing . parseSourcesList) text
+       return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+verifySourcesList :: MonadApt m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifySourceLine :: MonadApt m => Maybe EnvRoot -> String -> m Slice
+verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str)
+
+verifyDebSource :: MonadApt m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    repo >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      repo =
+          case uriScheme (sourceUri line) of
+            "file:" -> prepareRepository (Local (EnvPath chroot' (uriPath (sourceUri line))))
+            _ -> prepareRepository (Remote (toURI' (sourceUri line)))
+      chroot' = fromMaybe (EnvRoot "") chroot
diff --git a/testdata/merge2-expected/Debian/Repo/SourcesList.hs b/testdata/merge2-expected/Debian/Repo/SourcesList.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/SourcesList.hs
@@ -0,0 +1,142 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+--- | A DebSource represents a release of a remote repository and a
+--- method for accessing that repository.
+module Debian.Repo.SourcesList
+    (parseSourceLine,   -- String -> DebSource
+     parseSourceLine',  -- String -> Maybe DebSource
+     parseSourcesList,  -- String -> [DebSource]
+     quoteWords -- String -> [String]
+    )
+    where
+
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Sources (DebSource(..), SourceType(..))
+import Network.URI (parseURI, unEscapeString)
+
+{-
+
+deb uri distribution [component1] [componenent2] [...]
+
+The URI for the deb type must specify the base of the Debian
+distribution, from which APT will find the information it needs.
+
+distribution can specify an exact path, in which case the components
+must be omitted and distribution must end with a slash (/).
+
+If distribution does not specify an exact path, at least one component
+must be present.
+
+Distribution may also contain a variable, $(ARCH), which expands to
+the Debian architecture (i386, m68k, powerpc, ...)  used on the
+system.
+
+The rest of the line can be marked as a comment by using a #.
+
+Additional Notes:
+
+ + Lines can begin with leading white space.
+
+ + If the dist ends with slash (/), then it must be an absolute path
+   and it is an error to specify components after it.
+
+-}
+
+-- |quoteWords - similar to words, but with special handling of
+-- double-quotes and brackets.
+--
+-- The handling double quotes and [] is supposed to match:
+-- apt-0.6.44.2\/apt-pkg\/contrib\/strutl.cc:ParseQuoteWord()
+--
+-- The behaviour can be defined as:
+--
+--  Break the string into space seperated words ignoring spaces that
+--  appear between \"\" or []. Strip trailing and leading white space
+--  around words. Strip out double quotes, but leave the square
+--  brackets intact.
+quoteWords :: String -> [String]
+quoteWords [] = []
+quoteWords s = quoteWords' (dropWhile (==' ') s)
+    where
+      quoteWords' :: String -> [String]
+      quoteWords' [] = []
+      quoteWords' str =
+          case break (flip elem " [\"") str of
+            ([],[]) -> []
+            (w, []) -> [w]
+            (w, (' ':rest)) -> w : (quoteWords' (dropWhile (==' ') rest))
+            (w, ('"':rest)) ->
+                case break (== '"') rest of
+                  (w',('"':rest)) ->
+                      case quoteWords' rest of
+                        [] ->  [w ++ w']
+                        (w'':ws) -> ((w ++ w' ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing \" in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            (w, ('[':rest)) ->
+                case break (== ']') rest of
+                  (w',(']':rest)) ->
+                      case quoteWords' rest of
+                        []       -> [w ++ "[" ++ w' ++ "]"]
+                        (w'':ws) -> ((w ++ "[" ++ w' ++ "]" ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing ] in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            _ -> error ("the impossible happened in SourcesList.quoteWords")
+
+stripLine :: String -> String
+stripLine = takeWhile (/= '#') . dropWhile (== ' ')
+
+sourceLines :: String -> [String]
+sourceLines = filter (not . null) . map stripLine . lines
+
+-- |parseSourceLine -- parses a source line
+-- the argument must be a non-empty, valid source line with comments stripped
+-- see: 'sourceLines'
+parseSourceLine :: String -> DebSource
+parseSourceLine str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Deb
+                          "deb-src" -> DebSrc
+                          o -> error ("parseSourceLine: invalid type " ++ o ++ " in line:\n" ++ str)
+              theUri = case parseURI theUriStr of
+                         Nothing -> error ("parseSourceLine: invalid uri " ++ theUriStr ++ " in the line:\n" ++ str)
+                         Just u -> u
+              theDist = unEscapeString theDistStr
+          in
+            case last theDist of
+              '/' -> if null sections
+                      then DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              _ -> if null sections
+                    then error ("parseSourceLine: Dist is not an exact path, so at least one section is required on the line:\n" ++ str)
+                    else DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Right (parseReleaseName theDist, sections) }
+      _ -> error ("parseSourceLine: invalid line in sources.list:\n" ++ str)
+
+parseSourceLine' :: String -> Maybe DebSource
+parseSourceLine' str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Just Deb
+                          "deb-src" -> Just DebSrc
+                          _ -> Nothing
+              theUri = case parseURI theUriStr of
+                         Nothing -> Nothing
+                         Just u -> Just u
+              theDist = unEscapeString theDistStr
+          in
+            case (last theDist, theType, theUri) of
+              ('/', Just typ, Just uri) -> if null sections
+                      then Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              (_, Just typ, Just uri) -> if null sections
+                    then Nothing
+                    else Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Right ((parseReleaseName theDist), sections) }
+              _ -> Nothing
+      _ -> Nothing
+
+parseSourcesList :: String -> [DebSource]
+parseSourcesList = map parseSourceLine . sourceLines
diff --git a/testdata/merge2-expected/Debian/Repo/Sync.hs b/testdata/merge2-expected/Debian/Repo/Sync.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Sync.hs
@@ -0,0 +1,52 @@
+-- Comment above module head
+module Debian.Repo.Sync {-# WARNING "this is a warning" #-}
+    ( rsync
+    , foo
+    -- Comment after last export
+    ) where
+
+-- Comment before first import
+
+import Control.Monad.Trans (MonadIO)
+import qualified Data.ByteString as B (empty)
+import System.Exit (ExitCode)
+import System.FilePath (dropTrailingPathSeparator)
+-- Comment between two imporrts
+import System.Process (proc)
+import System.Process.Progress (keepResult, runProcessF)
+
+-- Comment before first decl
+rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode
+rsync extra source dest =
+    do result <- runProcessF (proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                                            [dropTrailingPathSeparator source ++ "/",
+                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult
+       case result of
+         [x] -> return x
+         _ -> error "Missing or multiple exit codes"
+
+-- Comment between two decls
+foo :: Int
+foo = 1
+
+{-
+handleExit 1 = "Syntax or usage error"
+handleExit 2 = "Protocol incompatibility"
+handleExit 3 = "Errors selecting input/output files, dirs"
+handleExit 4 = "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+handleExit 5 = "Error starting client-server protocol"
+handleExit 6 = "Daemon unable to append to log-file"
+handleExit 10 = "Error in socket I/O"
+handleExit 11 = "Error in file I/O"
+handleExit 12 = "Error in rsync protocol data stream"
+handleExit 13 = "Errors with program diagnostics"
+handleExit 14 = "Error in IPC code"
+handleExit 20 = "Received SIGUSR1 or SIGINT"
+handleExit 21 = "Some error returned by waitpid()"
+handleExit 22 = "Error allocating core memory buffers"
+handleExit 23 = "Partial transfer due to error"
+handleExit 24 = "Partial transfer due to vanished source files"
+handleExit 25 = "The --max-delete limit stopped deletions"
+handleExit 30 = "Timeout in data send/receive"
+handleExit 35 = "Timeout waiting for daemon connection"
+-}
diff --git a/testdata/merge2-expected/Debian/Repo/Types.hs b/testdata/merge2-expected/Debian/Repo/Types.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types.hs
@@ -0,0 +1,11 @@
+module Debian.Repo.Types
+    ( module Debian.Repo.Types.AptImage
+    , module Debian.Repo.Types.Common
+    , module Debian.Repo.Types.PackageIndex
+    , module Debian.Repo.Types.Release
+    ) where
+
+import Debian.Repo.Types.AptImage
+import Debian.Repo.Types.Common
+import Debian.Repo.Types.PackageIndex
+import Debian.Repo.Types.Release
diff --git a/testdata/merge2-expected/Debian/Repo/Types/AptBuildCache.hs b/testdata/merge2-expected/Debian/Repo/Types/AptBuildCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/AptBuildCache.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptBuildCache
+    ( AptBuildCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache)
+import Debian.Repo.Types.Common (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Common (SliceList)
+
+-- | An apt cache with extra sources.list lines for a local upload repository.
+class AptCache t => AptBuildCache t where
+    -- | The sources.list
+    aptSliceList :: t -> SliceList
diff --git a/testdata/merge2-expected/Debian/Repo/Types/AptCache.hs b/testdata/merge2-expected/Debian/Repo/Types/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/AptCache.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptCache
+    ( AptCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.Common (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Common (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+class (Ord t, Eq t, Show t) => AptCache t where
+    globalCacheDir :: t -> FilePath
+    -- | The directory you might chroot to.
+    rootDir :: t -> EnvRoot
+    -- | The sources.list without the local repository
+    aptBaseSliceList :: t -> SliceList
+    -- | The build architecture
+    aptArch :: t -> Arch
+    -- | Return the all source packages in this AptCache.
+    aptSourcePackages :: t -> [SourcePackage]
+    -- | Return the all binary packages for the architecture of this AptCache.
+    aptBinaryPackages :: t -> [BinaryPackage]
+    -- | Name of release
+    aptReleaseName :: t -> ReleaseName
diff --git a/testdata/merge2-expected/Debian/Repo/Types/AptImage.hs b/testdata/merge2-expected/Debian/Repo/Types/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/AptImage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptImage
+    ( AptImage(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache(..))
+import Debian.Repo.Types.Common (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import qualified Debian.Repo.Types.Common as T (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+data AptImage =
+    AptImage { aptGlobalCacheDir :: FilePath
+             , aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSliceList :: T.SliceList
+             , aptImageReleaseName :: ReleaseName
+             , aptImageSourcePackages :: [SourcePackage]
+             , aptImageBinaryPackages :: [BinaryPackage]
+             }
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (aptImageReleaseName apt)
+
+instance AptCache AptImage where
+    globalCacheDir = aptGlobalCacheDir
+    rootDir = aptImageRoot
+    aptArch = aptImageArch
+    aptBaseSliceList = aptImageSliceList
+    aptSourcePackages = aptImageSourcePackages
+    aptBinaryPackages = aptImageBinaryPackages
+    aptReleaseName = aptImageReleaseName
+
+instance Ord AptImage where
+    compare a b = compare (aptImageReleaseName a) (aptImageReleaseName b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
diff --git a/testdata/merge2-expected/Debian/Repo/Types/Common.hs b/testdata/merge2-expected/Debian/Repo/Types/Common.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/Common.hs
@@ -0,0 +1,128 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Common
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    , Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    , EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+import Debian.Sources (DebSource(..), SliceName(..), SourceType(..))
+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), vcat)
+
+
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Text (unpack)
+import Debian.Repo.Types.Release (Release)
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+
+
+ 
+data Slice = Slice{sliceRepoKey ::
+                   Debian.Repo.Types.Common.RepoKey,
+                   sliceSource :: DebSource}
+           deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty SliceList where
+    pretty = vcat . map (pretty . sliceSource) . slices
+
+deriving instance Show SourceType
+deriving instance Show DebSource
+
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
+
+
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
+
diff --git a/testdata/merge2-expected/Debian/Repo/Types/PackageIndex.hs b/testdata/merge2-expected/Debian/Repo/Types/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/PackageIndex.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.PackageIndex
+    ( PackageIndex(..)
+    , PackageIndexLocal
+    , PackageID(packageVersion)
+    , prettyPackageID
+    , BinaryPackage(..)
+    , binaryPackageName
+    , prettyBinaryPackage
+    , makeBinaryPackageID
+    , SourcePackage(..)
+    , sourcePackageName
+    , makeSourcePackageID
+    , SourceControl(..)
+    , SourceFileSpec(..)
+    , PackageIDLocal
+    , BinaryPackageLocal
+    , SourcePackageLocal
+    , PackageVersion(..)
+    , PkgVersion(..)
+    , prettyPkgVersion
+    ) where
+
+import Data.Text (Text, map)
+import Debian.Arch (Arch(..))
+import qualified Debian.Control.Text as T (Paragraph)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName, Relations)
+import Debian.Release (Section(..))
+import Debian.Repo.Orphans ({- instances -})
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)
+
+class (Eq a, Ord a) => PackageVersion a where
+    pkgName :: a -> BinPkgName
+    pkgVersion :: a -> DebianVersion
+
+-- |This is an old type which is still used to interface with the
+-- Debian.Relation module.
+data PkgVersion = PkgVersion { getName :: BinPkgName
+                             , getVersion :: DebianVersion
+                             } deriving (Eq, Ord, Show)
+
+instance PackageVersion PkgVersion where
+    pkgName = getName
+    pkgVersion = getVersion
+
+prettyPkgVersion :: PkgVersion -> Doc
+prettyPkgVersion v = pretty (getName v) <> text "=" <> prettyDebianVersion (getVersion v)
+
+---------------- PACKAGES AND PACKAGE INDEXES -------------
+
+-- |The PackageIndex type represents a file containing control
+-- information about debian packages, either source or binary.
+-- Though the control information for a binary package does not
+-- specify an architecture, the architecture here is that of
+-- the environment where the package information is cached.
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+type PackageIndexLocal = PackageIndex
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage p = pretty (pkgName p) <> text "-" <> prettyDebianVersion (pkgVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
+
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+
+-- | The 'PackageID' type fully identifies a package by name, version,
+-- and a 'PackageIndex' which identifies the package's release,
+-- component and architecture.
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+binaryPackageName :: BinaryPackage -> BinPkgName
+binaryPackageName = packageName . packageID
+
+sourcePackageName :: SourcePackage -> SrcPkgName
+sourcePackageName = packageName . sourcePackageID
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = pretty (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      }
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+type PackageIDLocal n = PackageID n
+type BinaryPackageLocal = BinaryPackage
+type SourcePackageLocal = SourcePackage
diff --git a/testdata/merge2-expected/Debian/Repo/Types/Release.hs b/testdata/merge2-expected/Debian/Repo/Types/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/Release.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , makeReleaseInfo
+    , parseComponents
+    , parseArchitectures
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure))
+import Data.Text (Text, unpack)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (fieldValue, Paragraph)
+import Debian.Release (parseSection', ReleaseName(..), Section(..))
+import Text.Regex (mkRegex, splitRegex)
+import qualified Tmp.File as F (File(..))
+
+-- FIXME: The lists here should be sets so that == and compare work properly.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: [Arch]
+                       , releaseComponents :: [Section]
+                       } deriving (Eq, Ord, Read, Show)
+
+makeReleaseInfo :: F.File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(F.File {F.text = Failure msgs}) _name _aliases =
+    error $ "Failure reading " ++ show (F.path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(F.File {F.text = Success info}) name aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = name
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (F.path file)
+
+parseArchitectures :: Text -> [Arch]
+parseArchitectures archList =
+    map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
diff --git a/testdata/merge2-expected/Debian/Repo/Types/Repository.hs b/testdata/merge2-expected/Debian/Repo/Types/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Debian/Repo/Types/Repository.hs
@@ -0,0 +1,431 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repository
+    ( Repository
+    , LocalRepository, repoRoot, repoLayout, repoReleaseInfoLocal
+    , RemoteRepository
+    , Layout(..)
+    , MonadRepoCache(getRepoCache, putRepoCache)
+    , loadRepoCache
+    , saveRepoCache
+    , fromLocalRepository
+    , readLocalRepo
+    , prepareLocalRepository
+    , copyLocalRepo -- repoCD
+    , prepareRepository
+    , setRepositoryCompatibility
+    , flushLocalRepository
+    , poolDir'
+    -- , sliceReleaseNames
+    , repoReleaseNames
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure), maybeRead)
+import Control.Exception (ErrorCall(..), SomeException, toException)
+import Control.Monad (filterM, unless, when)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IO (catch)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.List (groupBy, intercalate, isPrefixOf, partition, sort)
+import Data.Map as Map (empty, fromList, insert, insertWith, lookup, Map, toList, union)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (Text, unpack)
+import Debian.Changes (ChangedFileSpec(changedFileSection), ChangesFile(changeInfo))
+import qualified Debian.Control.Text as T (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph, Paragraph')
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section, sectionName', SubSection(section))
+import Debian.Repo.Monads.Top (MonadTop, sub)
+import Debian.Repo.Sync (rsync)
+import Debian.Repo.Types.Common
+       (EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Types.Release (makeReleaseInfo, Release(releaseName))
+import Debian.Repo.Types.Common
+       (compatibilityFile, libraryCompatibilityLevel, Repo(..),
+        RepoKey(..))
+import Debian.URI (dirFromURI, fileFromURI, fromURI', toURI', URI(uriScheme, uriPath), URI', uriToString')
+import Debian.UTF8 as Deb (decode)
+import Extra.Files (maybeWriteFile)
+import Extra.List (partitionM)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink, setFileMode)
+import System.Process.Progress (qPutStr, qPutStrLn, quieter)
+import System.Unix.Directory (removeRecursiveSafely)
+import qualified Text.Format as F (Pretty(..))
+import Text.PrettyPrint.ANSI.Leijen (text)
+import Text.Regex (matchRegex, mkRegex)
+import qualified Tmp.File as F (File(..), readFile, Source(RemotePath))
+
+--------------------- REPOSITORY -----------------------
+
+-- | The Repository type reprents any instance of the Repo class, so
+-- it might be local or remote.
+--data Repository = forall a. (Repo a) => Repository a
+data Repository
+    = LocalRepo LocalRepository
+    | RemoteRepo RemoteRepository
+    deriving (Show, Read)
+
+instance Ord Repository where
+    compare a b = compare (repoKey a) (repoKey b)
+
+instance Eq Repository where
+    a == b = compare a b == EQ
+
+-- | URI has a bogus show function, which we are using here.
+instance F.Pretty URI' where
+    pretty = text . show . fromURI'
+
+instance F.Pretty Repository where
+    pretty (LocalRepo r) = text $ outsidePath (repoRoot r)
+    pretty (RemoteRepo r) = F.pretty r
+
+instance Repo Repository where
+    repoKey (LocalRepo (LocalRepository path _ _)) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoKey (RemoteRepo (RemoteRepository uri _)) = Remote uri
+    repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
+    repoReleaseInfo (RemoteRepo (RemoteRepository _ info)) = info
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot_ :: EnvPath
+      , repoLayout_ :: (Maybe Layout)
+      , repoReleaseInfoLocal_ :: [Release]
+      } deriving (Read, Show)
+
+instance Ord LocalRepository where
+    compare (LocalRepository a _ _) (LocalRepository b _ _) = compare a b
+
+instance Eq LocalRepository where
+    a == b = compare a b == EQ
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show)
+
+instance Ord RemoteRepository where
+    compare (RemoteRepository a _) (RemoteRepository b _) = compare a b
+
+instance Eq RemoteRepository where
+    a == b = compare a b == EQ
+
+instance F.Pretty RemoteRepository where
+    pretty (RemoteRepository s _) = F.pretty s
+
+repoRoot :: LocalRepository -> EnvPath
+repoRoot = repoRoot_
+
+repoLayout :: LocalRepository -> Maybe Layout
+repoLayout = repoLayout_
+
+repoReleaseInfoLocal :: LocalRepository -> [Release]
+repoReleaseInfoLocal = repoReleaseInfoLocal_
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+class MonadIO m => MonadRepoCache m where
+    getRepoCache :: m (Map RepoKey Repository)
+    putRepoCache :: Map RepoKey Repository -> m ()
+
+modifyRepoCache :: MonadRepoCache m => (Map RepoKey Repository -> Map RepoKey Repository) -> m ()
+modifyRepoCache f = do
+    s <- getRepoCache
+    putRepoCache (f s)
+
+fromLocalRepository :: LocalRepository -> Repository
+fromLocalRepository = LocalRepo
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: (MonadRepoCache m, MonadTop m) => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty)) >>= putRepoCache
+    where
+      loadRepoCache' :: FilePath -> IO (Map RepoKey Repository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: (MonadIO m, MonadTop m, MonadRepoCache m) => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- getRepoCache
+             repoCache <- liftIO $ loadCache path
+             let merged = show . Map.toList $ Map.union live repoCache
+             liftIO (F.removeLink path `IO.catch` (\e -> unless (isDoesNotExistError e) (ioError e))) >> liftIO (writeFile path merged)
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map RepoKey Repository)
+            loadCache path =
+                readFile path `IO.catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+readLocalRepo :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+readLocalRepo root layout =
+    do state <- getRepoCache
+       case Map.lookup (Local root) state of
+         Just (RemoteRepo (RemoteRepository _ _)) -> error "readLocalRepo: internal error" -- somehow a remote repo got associated with a Local RepoKey
+         Just (LocalRepo repo) -> return repo
+         Nothing ->
+             do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+                (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+                linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+                let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+                let distGroups = groupBy fstEq . sort $ aliasPairs
+                let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+                releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+                qPutStrLn ("LocalRepository releaseInfo " ++ show root ++ ": " ++ show releaseInfo)
+                let repo = LocalRepository { repoRoot_ = root
+                                           , repoLayout_ = layout
+                                           , repoReleaseInfoLocal_ = releaseInfo }
+                putRepoCache (insertWith (\ _ x -> x) (Local root) (LocalRepo repo) state)
+                return repo
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist ++ "/Release"
+      distDir = outsidePath root ++ "/dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path dist aliases =
+    liftIO (F.readFile path) >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> F.File Text -> Release
+parseRelease name aliases file =
+    case F.text file of
+      Failure msgs -> error $ "Could not read " ++ show (F.path file) ++ ": " ++ show msgs
+      Success text ->
+          case T.parseControl (show (F.path file)) text of
+            Left msg -> error $ "Failure parsing " ++ show (F.path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (F.path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (F.File {F.path = F.path file, F.text = Success info}) name aliases
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository root layout =
+    do mapM_ (liftIO . initDir)
+                 [(".", 0o40755),
+                  ("dists", 0o40755),
+                  ("incoming", 0o41755),
+                  ("removed", 0o40750),
+                  ("reject", 0o40750)]
+       layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+                  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+       mapM_ (liftIO . initDir)
+                 (case layout' of
+                    Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+                    Just Flat -> []
+                    Nothing -> [])
+       readLocalRepo root layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+{-      notSymbolicLink root name =
+          getSymbolicLinkStatus (root ++ "/dists/" ++ name) >>= return . not . isSymbolicLink
+      hasReleaseFile root name =
+          doesFileExist (root ++ "/dists/" ++ name ++ "/Release") -}
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadRepoCache m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do qPutStrLn ("Syncing local repository from " ++ src ++ " -> " ++ dst)
+       liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       result <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         ExitSuccess ->
+             do let repo' = repo {repoRoot_ = dest}
+                modifyRepoCache (Map.insert (Local dest) (LocalRepo repo'))
+                return repo'
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path text
+    where text = show libraryCompatibilityLevel ++ "\n"
+          path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepoCache m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       prepareLocalRepository (repoRoot r) (repoLayout r)
+
+prepareRepository :: MonadRepoCache m => RepoKey -> m Repository
+prepareRepository key =
+    case key of
+      Local path -> prepareLocalRepository path Nothing >>= return . LocalRepo
+      Remote uri -> prepareRepository' (fromURI' uri)
+
+-- | Prepare a repository, which may be remote or local depending on
+-- the URI.
+prepareRepository' :: MonadRepoCache m => URI -> m Repository
+prepareRepository' uri =
+    do state <- getRepoCache
+       repo <- maybe newRepo return (Map.lookup (Remote (toURI' uri)) state)
+       putRepoCache (Map.insert (Remote (toURI' uri)) repo state)
+       return repo
+    where
+      newRepo =
+             case uriScheme uri of
+               "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= return . LocalRepo
+               -- FIXME: We only want to verifyRepository on demand.
+               _ -> verifyRepository (toURI' uri)
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+{-# NOINLINE verifyRepository #-}
+verifyRepository :: MonadRepoCache m => URI' -> m Repository
+verifyRepository uri =
+    do state <- getRepoCache
+       case Map.lookup (Remote uri) state of
+         Just repo -> return repo
+         Nothing ->
+             do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+                let repo = RemoteRepo (RemoteRepository uri releaseInfo)
+                modifyRepoCache (Map.insert (Remote uri) repo)
+                return repo
+
+-- Nice code to do caching, but I figured out how to fix the old code.
+
+--instance Read URI where
+--    readsPrec _ s = [(fromJust (parseURI s), "")]
+--
+---- |Get the list of releases of a remote repository.
+--getReleaseInfo :: FilePath
+--               -> Bool     -- ^ If False don't look at existing cache
+--               -> URI
+--               -> IO [ReleaseInfo]
+--getReleaseInfo top tryCache uri =
+--    readCache >>= \ cache ->
+--    return (lookup uri cache) >>=
+--    maybe (updateCache cache) return
+--    where
+--      cachePath = top ++ "/repoCache"
+--      readCache :: IO [(URI, [ReleaseInfo])]
+--      readCache = if tryCache
+--                  then try (readFile cachePath >>= return . read) >>= return . either (\ (_ :: SomeException) -> []) id
+--                  else return []
+--      updateCache :: [(URI, [ReleaseInfo])] -> IO [ReleaseInfo]
+--      updateCache pairs = getReleaseInfoRemote uri >>= \ info ->
+--                          writeCache ((uri, info) : pairs) >> return info
+--      writeCache :: [(URI, [ReleaseInfo])] -> IO ()
+--      writeCache pairs = writeFile (show pairs) cachePath
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    quieter 2 (dirFromURI distsURI) >>=
+    quieter 2 . either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [F.File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (F.File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: F.File (T.Paragraph' Text) -> Maybe Text
+      getSuite (F.File {F.text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (F.File {F.text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (F.File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ F.File {F.path = F.RemotePath releaseURI, F.text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
+
+repoReleaseNames :: Repository -> [ReleaseName]
+repoReleaseNames (RemoteRepo (RemoteRepository _ rels)) = map releaseName rels
+repoReleaseNames (LocalRepo (LocalRepository _ _ rels)) = map releaseName rels
diff --git a/testdata/merge2-expected/Text/Format.hs b/testdata/merge2-expected/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/merge2-expected/Tmp/File.hs b/testdata/merge2-expected/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge2-expected/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
diff --git a/testdata/merge3-expected/Debian/Repo/AptCache.hs b/testdata/merge3-expected/Debian/Repo/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/AptCache.hs
@@ -0,0 +1,393 @@
+{- This is a comment at the top -}   
+{-# LANGUAGE DeriveDataTypeable, PackageImports #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |An AptCache represents a local cache of a remote repository.  The
+-- cached information is usually downloaded by running "apt-get
+-- update", and appears in @\/var\/lib\/apt\/lists@.
+module Debian.Repo.AptCache {-# WARNING "this is a warning" #-}
+    ( SourcesChangedAction(..)
+    , aptSourcePackagesSorted
+    , sliceIndexes
+    , cacheDistDir
+    , distDir
+    , aptDir
+    , cacheRootDir
+    , cacheSourcesPath
+    , sourcesPath
+    , sourceDir
+    , aptCacheFiles
+    , aptCacheFilesOfSlice
+    , archFiles
+    , buildArchOfEnv
+    , buildArchOfRoot
+    , updateCacheSources
+    , sourcePackages
+    , binaryPackages
+    , runAptGet
+    , aptOpts
+    , getSourcePackagesBase
+    , getBinaryPackagesBase
+    , getSourcePackagesBuild
+    , getBinaryPackagesBuild
+    ) where
+
+import Control.DeepSeq (force, NFData)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Data (Data)
+import Data.List (intercalate, sortBy)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Relation (BinPkgName, PkgName, SrcPkgName(..))
+import Debian.Release (ReleaseName(relName), releaseName', sectionName')
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.Package (binaryPackagesOfCachedIndex, sourcePackagesOfCachedIndex)
+import Debian.Repo.Slice (binarySlices, sourceSlices, verifySourcesList)
+import Debian.Repo.SourcesList (parseSourcesList)
+import Debian.Repo.Types.AptBuildCache (AptBuildCache(aptSliceList))
+import Debian.Repo.Types.AptCache (AptCache(..), AptCache(aptArch, aptBaseSliceList, aptBinaryPackages, aptReleaseName, aptSourcePackages, globalCacheDir))
+import Debian.Repo.Types.Slice (EnvRoot(..), EnvRoot(EnvRoot))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID), binaryPackageName, PackageID(packageVersion), PackageIndex(..), SourcePackage(sourcePackageID), sourcePackageName)
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Slice
+       (Repo(repoReleaseInfo), repoKey, RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache, prepareRepository)
+import Debian.Repo.Types.Slice (Slice(..), SliceList(slices))
+import Debian.Sources (DebSource(..), SourceType(..))
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Extra.Files (replaceFile)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetLine, stdin)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Progress (ePutStr, ePutStrLn, qPutStrLn, runProcessF)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+instance NFData ExitCode
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+-- | A directory which will hold all the cached files for this
+-- NamedSliceList.
+cacheDistDir :: FilePath -> ReleaseName -> FilePath
+cacheDistDir cacheDir release = cacheDir ++ "/dists/" ++ relName release
+
+cacheRootDir :: FilePath -> ReleaseName -> EnvRoot
+cacheRootDir cacheDir release = EnvRoot (cacheDistDir cacheDir release ++ "/aptEnv")
+
+distDir :: AptCache c => c -> FilePath
+distDir cache = cacheDistDir (globalCacheDir cache) (aptReleaseName cache)
+
+aptDir :: AptCache c => c -> SrcPkgName -> FilePath
+aptDir cache package = distDir cache ++ "/apt/" ++ unSrcPkgName package
+
+-- | The path where a text of the SliceList is stored.
+cacheSourcesPath :: FilePath -> ReleaseName -> FilePath
+cacheSourcesPath cacheDir release = cacheDistDir cacheDir release </> "sources"
+
+sourcesPath :: AptCache c => c -> FilePath
+sourcesPath cache = cacheSourcesPath (globalCacheDir cache) (aptReleaseName cache)
+
+-- Additional functions which can only be used on already constructed
+-- instances of AptCache.
+
+-- | A directory holding all files downloaded by apt-get source for a
+-- certain package
+sourceDir :: AptCache t => t -> String -> FilePath
+sourceDir c package = distDir c ++ "/apt/" ++ package
+
+-- |Return all the named source packages sorted by version
+aptSourcePackagesSorted :: AptCache t => t -> [SrcPkgName] -> [SourcePackage]
+aptSourcePackagesSorted os names =
+    sortBy cmp . filterNames names . aptSourcePackages $ os
+    where
+      filterNames names' packages =
+          filter (flip elem names' . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: (MonadRepoCache m, AptCache a) => a -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes cache slice =
+    prepareRepository (sliceRepoKey slice) >>= \ repo ->
+    case (sourceDist (sliceSource slice)) of
+      Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+      Right (release, sections) -> return $ map (makeIndex repo release) sections
+    where
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> aptArch cache })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+-- |Return the paths in the local cache of the index files of a slice list.
+aptCacheFiles :: AptCache a => a -> [DebSource] -> [FilePath]
+aptCacheFiles apt sources = concat . map (aptCacheFilesOfSlice apt) $ sources
+
+-- |Return the paths in the local cache of the index files of a single slice.
+aptCacheFilesOfSlice :: AptCache a => a -> DebSource -> [FilePath]
+aptCacheFilesOfSlice apt slice = archFiles (aptArch apt) slice
+
+-- |Return the list of files that apt-get update would write into
+-- \/var\/lib\/apt\/lists when it processed the given list of DebSource.
+archFiles :: Arch -> DebSource -> [FilePath]
+archFiles arch deb =
+    case (arch, deb) of
+      (Binary _ _, DebSource DebSrc _ _) ->
+          map (++ "_source_Sources") (archFiles' deb)
+      (Binary _os _cpu, DebSource Deb _ _) ->
+          map (++ ("_binary-" ++ show (prettyArch arch) ++ "_Packages")) (archFiles' deb)
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+archFiles' :: DebSource -> [FilePath]
+archFiles' deb =
+    let uri = sourceUri deb
+        distro = sourceDist deb in
+    let scheme = uriScheme uri
+        auth = uriAuthority uri
+        path = uriPath uri in
+    let userpass = maybe "" uriUserInfo auth
+        reg = maybeOfString $ maybe "" uriRegName auth
+        port = maybe "" uriPort auth in
+    let (user, pass) = break (== ':') userpass in
+    let user' = maybeOfString user
+        pass' = maybeOfString pass in
+    let uriText = prefix scheme user' pass' reg port path in
+    -- what about dist?
+    either (\ exact -> [(escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText ++ escape exact))])
+           (\ (dist, sections) ->
+                map (\ section ->
+                         (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+                          releaseName' dist ++ "_" ++ sectionName' section))
+                    sections)
+           distro
+    where
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid DebSource: " ++ show (pretty deb))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+
+buildArchOfEnv :: EnvRoot -> IO Arch
+buildArchOfEnv (EnvRoot root)  =
+    do setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+       a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (ArchOS os) (ArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+{-
+  (err, _) <- useEnv root forceList (readProcessChunks (shell cmd) L.empty) >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "export LOGNAME=root; dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+{-
+    do (code, out, err, _) <- runProcess (shell cmd) L.empty >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s =
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.
+updateCacheSources :: (MonadApt m, AptCache c) => SourcesChangedAction -> c -> m c
+updateCacheSources sourcesChangedAction distro =
+    -- (\ x -> qPutStrLn "Updating cache sources" >> quieter 2 x) $
+    qPutStrLn "Updating cache sources" >>
+    do
+      let baseSources = aptBaseSliceList distro
+      --let distro@(ReleaseCache _ dist _) = releaseFromConfig' top text
+      let dir = Debian.Repo.AptCache.distDir distro
+      distExists <- liftIO $ doesFileExist (Debian.Repo.AptCache.sourcesPath distro)
+      case distExists of
+        True ->
+            do
+              fileSources <- liftIO (readFile (Debian.Repo.AptCache.sourcesPath distro)) >>= verifySourcesList Nothing . parseSourcesList
+              case (fileSources == baseSources, sourcesChangedAction) of
+                (True, _) -> return ()
+                (False, SourcesChangedError) ->
+                    do
+                      ePutStrLn ("The sources.list in the existing '" ++ relName (aptReleaseName distro) ++
+                                 "' build environment doesn't match the parameters passed to the autobuilder" ++
+                                 ":\n\n" ++ Debian.Repo.AptCache.sourcesPath distro ++ ":\n\n" ++
+                                 show (pretty fileSources) ++
+                                 "\nRun-time parameters:\n\n" ++
+                                 show (pretty baseSources) ++ "\n" ++
+                                 "It is likely that the build environment in\n" ++
+                                 dir ++ " is invalid and should be rebuilt.")
+                      ePutStr $ "Remove it and continue (or exit)?  [y/n]: "
+                      result <- liftIO $ hGetLine stdin
+                      case result of
+                        ('y' : _) ->
+                            do
+                              liftIO $ removeRecursiveSafely dir
+                              liftIO $ createDirectoryIfMissing True dir
+                              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                        _ ->
+                            error ("Please remove " ++ dir ++ " and restart.")
+                (False, RemoveRelease) ->
+                    do
+                      ePutStrLn $ "Removing suspect environment: " ++ dir
+                      liftIO $ removeRecursiveSafely dir
+                      liftIO $ createDirectoryIfMissing True dir
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                (False, UpdateSources) ->
+                    do
+                      -- The sources.list has changed, but it should be
+                      -- safe to update it.
+                      ePutStrLn $ "Updating environment with new sources.list: " ++ dir
+                      liftIO $ removeFile (Debian.Repo.AptCache.sourcesPath distro)
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+        False ->
+            do
+              liftIO $ createDirectoryIfMissing True dir
+              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+      return distro
+
+-- | Return a sorted list of available source packages, newest version first.
+sourcePackages :: AptCache a => a -> [SrcPkgName] -> [SourcePackage]
+sourcePackages os names =
+    sortBy cmp . filterNames . aptSourcePackages $ os
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          filter (flip elem names . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+binaryPackages :: AptCache a => a -> [BinPkgName] -> [BinaryPackage]
+binaryPackages os names =
+    sortBy cmp . filterNames . aptBinaryPackages $ os
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          filter (flip elem names . binaryPackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
+
+getSourcePackagesBase :: (AptCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getSourcePackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBase :: (AptCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+-- | Note that apt-get source works for binary or source package names.
+runAptGet :: (PkgName n, AptCache t) => t -> FilePath -> String -> [(n, Maybe DebianVersion)] -> IO ()
+runAptGet os dir command packages =
+    createDirectoryIfMissing True dir >> runProcessF (shell cmd) L.empty >> return ()
+    where
+      cmd = (intercalate " " ("cd" : dir : "&&" : "apt-get" : aptOpts os : command : map formatPackage packages))
+      formatPackage (name, Nothing) = show (pretty name)
+      formatPackage (name, Just version) = show (pretty name) ++ "=" ++ show (prettyDebianVersion version)
+
+aptOpts :: AptCache t => t -> String
+aptOpts os =
+    (" -o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status" ++
+     " -o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists" ++
+     " -o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives" ++
+     " -o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list" ++
+     " -o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d")
+    where root = rootPath . rootDir $ os
diff --git a/testdata/merge3-expected/Debian/Repo/AptImage.hs b/testdata/merge3-expected/Debian/Repo/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/AptImage.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |The AptImage object represents a partial OS image which is capable
+-- of running apt-get, and thus obtaining repository info and source
+-- code packages.
+module Debian.Repo.AptImage
+    ( prepareAptEnv
+    , updateAptEnv
+    ) where
+
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Function (on)
+import Data.List (sortBy)
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.AptCache (aptOpts, buildArchOfRoot, cacheRootDir, getBinaryPackagesBase, getSourcePackagesBase, SourcesChangedAction, updateCacheSources)
+import Debian.Repo.Monads.Apt (insertAptImage, lookupAptImage, MonadApt(getApt, putApt))
+import Debian.Repo.Types.AptImage (AptImage(..))
+import Debian.Repo.Types.Slice (EnvRoot(..))
+import Debian.Repo.Types.PackageIndex (PackageID(packageVersion), SourcePackage(sourcePackageID))
+import Debian.Repo.Types.Slice
+       (NamedSliceList(sliceList, sliceListName))
+import Debian.Sources (SliceName(sliceName))
+import Extra.Files (replaceFile, writeFileIfMissing)
+import System.Directory (createDirectoryIfMissing)
+import System.Process (shell)
+import System.Process.Progress (qPutStrLn, quieter, runProcessF)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+prepareAptEnv :: MonadApt m =>
+                 FilePath               -- Put environment in a subdirectory of this
+              -> SourcesChangedAction   -- What to do if environment already exists and sources.list is different
+              -> NamedSliceList         -- The sources.list
+              -> m AptImage             -- The resulting environment
+prepareAptEnv cacheDir sourcesChangedAction sources =
+    (\ x -> qPutStrLn ("Preparing apt-get environment for " ++ show (sliceName (sliceListName sources))) >> quieter 2 x) $
+    getApt >>= return . lookupAptImage (sliceListName sources) >>=
+    maybe (prepareAptEnv' cacheDir sourcesChangedAction sources) return
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+{-# NOINLINE prepareAptEnv' #-}
+prepareAptEnv' :: MonadApt m => FilePath -> SourcesChangedAction -> NamedSliceList -> m AptImage
+prepareAptEnv' cacheDir sourcesChangedAction sources =
+    do let root = rootPath (cacheRootDir cacheDir (ReleaseName (sliceName (sliceListName sources))))
+       --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/cache/apt/archives/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/dpkg")
+       liftIO $ createDirectoryIfMissing True (root ++ "/etc/apt")
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/status") ""
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/diversions") ""
+       -- We need to create the local pool before updating so the
+       -- sources.list will be valid.
+       let sourceListText = show (pretty (sliceList sources))
+       -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+       liftIO $ replaceFile (root ++ "/etc/apt/sources.list") sourceListText
+       arch <- liftIO $ buildArchOfRoot
+       let os = AptImage { aptGlobalCacheDir = cacheDir
+                         , aptImageRoot = EnvRoot root
+                         , aptImageArch = arch
+                         , aptImageReleaseName = ReleaseName . sliceName . sliceListName $ sources
+                         , aptImageSliceList = sliceList sources
+                         , aptImageSourcePackages = []
+                         , aptImageBinaryPackages = [] }
+       os' <- updateCacheSources sourcesChangedAction os >>= updateAptEnv
+       getApt >>= putApt . insertAptImage (sliceListName sources) os'
+       return os'
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+{-# NOINLINE updateAptEnv #-}
+updateAptEnv :: MonadApt m => AptImage -> m AptImage
+updateAptEnv os =
+    liftIO (runProcessF (shell cmd) L.empty) >>
+    getSourcePackagesBase os >>= return . sortBy cmp >>= \ sourcePackages ->
+    getBinaryPackagesBase os >>= \ binaryPackages ->
+    return $ os { aptImageSourcePackages = sourcePackages
+                , aptImageBinaryPackages = binaryPackages }
+    where
+      cmd = "apt-get" ++ aptOpts os ++ " update"
+      -- Flip args to get newest version first
+      cmp = flip (compare `on` (packageVersion . sourcePackageID))
+{-
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+    putStrLn ("> " ++ cmd) >> system cmd >>= \ code ->
+    case code of
+      ExitSuccess -> return ()
+      ExitFailure n -> error $ cmd ++ " -> ExitFailure " ++ show n
+-}
diff --git a/testdata/merge3-expected/Debian/Repo/Monads/Apt.hs b/testdata/merge3-expected/Debian/Repo/Monads/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Monads/Apt.hs
@@ -0,0 +1,195 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Debian.Repo.Monads.Apt
+    ( MonadApt(..)
+    , AptIOT
+    , AptIO
+    -- * AptIO Monad
+    , runAptIO
+    , runAptT
+    , tryAB
+    , tryJustAB
+    -- * State
+    , AptState
+    , initState
+    , setRepoMap
+    , getRepoMap
+    , lookupRepository
+    , insertRepository
+    , lookupAptImage
+    , insertAptImage
+    , lookupSourcePackages
+    , insertSourcePackages
+    , lookupBinaryPackages
+    , insertBinaryPackages
+    , readParagraphs
+    , findRelease
+    , putRelease
+    , countTasks
+    ) where
+
+import Control.Exception (try)
+import Control.Exception as E (Exception, tryJust)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, mapStateT, MonadIO(..), MonadTrans(..), put, StateT(runStateT))
+import qualified Data.Map as Map (empty, insert, lookup, Map)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(parseControlFromHandle), Paragraph)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Slice (Repo(repoKey), RepoKey(..))
+import Debian.Repo.Types.Repository (MonadRepoCache(getRepoCache, putRepoCache), Repository)
+import Debian.Sources (SliceName)
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+import System.Process.Progress (ePutStrLn)
+import Text.Printf (printf)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+type AptIO = AptIOT IO
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository           -- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage       -- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage]) -- ^ The contents of a package index, and status of local cache of index file
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+-- |Perform an AptIO monad task in the IO monad.
+runAptIO :: AptIO a -> IO a
+runAptIO action = (runStateT action) initState >>= \ (a, _) -> return a
+
+runAptT :: Monad m => AptIOT m a -> m a
+runAptT action = (runStateT action) initState >>= return . fst
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryAB :: Exception e => AptIO a -> AptIO (Either e a)
+tryAB task =
+    do state <- get
+       mapStateT (try' state) task
+    where
+      try' state task' =
+          do result <- try task'
+             case result of
+               Left e -> return (Left e, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryJustAB :: Exception e => (e -> Maybe b) -> AptIO a -> AptIO (Either b a)
+tryJustAB f task =
+    do state <- get
+       mapStateT (tryJust' state) task
+    where
+      tryJust' state task' =
+          do result <- tryJust f task'
+             case result of
+               Left b -> return (Left b, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+setRepoMap :: Map.Map RepoKey Repository -> AptState -> AptState
+setRepoMap m state = state {repoMap = m}
+
+getRepoMap :: AptState -> Map.Map RepoKey Repository
+getRepoMap state = repoMap state
+
+lookupRepository :: RepoKey -> AptState -> Maybe Repository
+lookupRepository uri state = Map.lookup uri (repoMap state)
+
+insertRepository :: RepoKey -> Repository -> AptState -> AptState
+insertRepository uri repo state = state {repoMap = Map.insert uri repo (repoMap state)}
+
+lookupAptImage :: SliceName -> AptState -> Maybe AptImage
+lookupAptImage name state = Map.lookup  name (aptImageMap state)
+
+insertAptImage :: SliceName -> AptImage -> AptState -> AptState
+insertAptImage name image state = state {aptImageMap = Map.insert name image (aptImageMap state)}
+
+lookupSourcePackages :: FilePath -> AptState -> Maybe (FileStatus, [SourcePackage])
+lookupSourcePackages key state = Map.lookup key (sourcePackageMap state)
+
+insertSourcePackages :: FilePath -> (FileStatus, [SourcePackage]) -> AptState -> AptState
+insertSourcePackages key packages state = state {sourcePackageMap = Map.insert key packages (sourcePackageMap state)}
+
+lookupBinaryPackages :: FilePath -> AptState -> Maybe (FileStatus, [BinaryPackage])
+lookupBinaryPackages key state = Map.lookup key (binaryPackageMap state)
+
+insertBinaryPackages :: FilePath -> (FileStatus, [BinaryPackage]) -> AptState -> AptState
+insertBinaryPackages key packages state =
+    state {binaryPackageMap = Map.insert key packages (binaryPackageMap state)}
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)                 -- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")     -- Debugging output
+       return paragraphs
+
+findRelease :: Repository -> ReleaseName -> AptState -> Maybe Release
+findRelease repo dist state =
+    Map.lookup (repoKey repo, dist) (releaseMap state)
+
+putRelease :: Repository -> ReleaseName -> Release -> AptState -> AptState
+putRelease repo dist release state =
+    state {releaseMap = Map.insert (repoKey repo, dist) release (releaseMap state)}
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/merge3-expected/Debian/Repo/Monads/Top.hs b/testdata/merge3-expected/Debian/Repo/Monads/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Monads/Top.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Monads.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    ) where
+
+import Control.Monad.Reader (MonadReader(ask), ReaderT(runReaderT))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class Monad m => MonadTop m where
+    askTop :: m FilePath
+
+instance Monad m => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
diff --git a/testdata/merge3-expected/Debian/Repo/Orphans.hs b/testdata/merge3-expected/Debian/Repo/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Orphans.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Orphans where
+
+import Data.Text (Text)
+import qualified Debian.Control.Text as T
+
+deriving instance Show (T.Field' Text)
+deriving instance Ord (T.Field' Text)
+deriving instance Show T.Paragraph
+deriving instance Ord T.Paragraph
diff --git a/testdata/merge3-expected/Debian/Repo/Package.hs b/testdata/merge3-expected/Debian/Repo/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Package.hs
@@ -0,0 +1,367 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package
+    ( sourceFilePaths
+    , binaryPackageSourceVersion
+    , binarySourceVersion
+    , sourcePackageBinaryNames
+    , sourceBinaryNames
+    , toSourcePackage
+    , toBinaryPackage
+    , binaryPackageSourceID
+    , sourcePackageBinaryIDs
+    , sourcePackagesOfIndex
+    , sourcePackagesOfCachedIndex
+    , binaryPackagesOfIndex
+    , binaryPackagesOfCachedIndex
+    , getPackages
+    , putPackages
+    , releaseSourcePackages
+    , releaseBinaryPackages
+    , TH.FixityDirection(..)
+    , TH.Fixity(..)
+    ) where
+
+import Control.Exception as E (catch, ErrorCall(..), SomeException(..), try)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString, fromChunks)
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, intersperse, map, partition)
+import Data.Maybe (catMaybes)
+import Data.Set as Set (fromList, map, Set, unions)
+import qualified Data.Text as T (concat, pack, Text, unpack)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Control (ControlFunctions(asString, stripWS), formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(lookupP), Field, Field'(Field), fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Monads.Apt (insertBinaryPackages, insertSourcePackages, lookupBinaryPackages, lookupSourcePackages, MonadApt(getApt, putApt), readParagraphs)
+import Debian.Repo.PackageIndex (binaryIndexList, packageIndexPath, sourceIndexList)
+import Debian.Repo.Types.AptCache (AptCache(aptArch, rootDir))
+import Debian.Repo.Types.Slice (EnvRoot(rootPath), outsidePath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(..), BinaryPackageLocal, binaryPackageName, makeBinaryPackageID, makeSourcePackageID, PackageID(..), PackageIndex(..), PackageIndexLocal, SourceControl(..), SourceFileSpec(SourceFileSpec, sourceFileName), SourcePackage(..))
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Slice (RepoKey, repoKeyURI)
+import Debian.Repo.Types.Repository (LocalRepository, MonadRepoCache, repoRoot)
+import Debian.URI (fileFromURIStrict)
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Extra.Files (writeAndZipFileWithBackup)
+import qualified Language.Haskell.TH.Syntax as TH (FixityDirection(..), Fixity(..))
+import Network.URI (escapeURIString, URI(..), URIAuth(..), uriToString)
+import System.FilePath ((</>), takeDirectory)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+useConstants :: Constants p => p -> Maybe Bool
+useConstants x = asBool x
+
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
+
+sourceFilePaths :: SourcePackage -> Set FilePath
+sourceFilePaths package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = binaryPackageName package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
+
+-- |Return the name and version number of the source package that
+-- generated this binary package.
+-- see also: 'binaryPackageSourceVersion'
+binarySourceVersion :: Paragraph' T.Text -> Maybe ((BinPkgName, DebianVersion), (String, DebianVersion))
+binarySourceVersion paragraph =
+    let mBinaryName = fmap (BinPkgName . T.unpack) $ B.fieldValue "Package" paragraph
+        mBinaryVersion = fmap (parseDebianVersion . T.unpack) $ B.fieldValue "Version" paragraph
+    in
+      case (mBinaryName, mBinaryVersion) of
+        (Just binaryName, Just binaryVersion) ->
+            fmap ((,) (binaryName, binaryVersion)) $ binarySourceVersion' binaryName binaryVersion paragraph
+        _ -> Nothing
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
+
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
+
+sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+sourceBinaryNames paragraph =
+    case B.fieldValue "Binary" paragraph of
+      Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID (PackageIndex component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+
+sourcePackageBinaryIDs :: Arch -> PackageIndex -> SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs Source _ _ = error "invalid argument"
+sourcePackageBinaryIDs arch sourceIndex package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfCachedIndex :: (AptCache a, MonadApt m) => a -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupSourcePackages path state
+       status <- liftIO $ getFileStatus path `E.catch` (\ (_ :: IOError) -> error $ "Sources.list seems out of sync.  If a new release has been created you probably need to remove " ++ takeDirectory (rootPath (rootDir cache)) ++ " and try again - sorry about that.")
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toSourcePackage index) paragraphs
+                 putApt (insertSourcePackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfCachedIndex :: (MonadApt m, AptCache a) => a -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupBinaryPackages path state
+       status <- liftIO $ getFileStatus path
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toBinaryPackage release index) paragraphs
+                 putApt (insertBinaryPackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+-- | Return a list of all source packages.
+releaseSourcePackages :: MonadRepoCache m => RepoKey -> Release -> m (Set SourcePackage)
+releaseSourcePackages repo release =
+    mapM (sourcePackagesOfIndex repo release) (sourceIndexList release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages :: MonadRepoCache m => RepoKey -> Release -> m (Set BinaryPackage)
+releaseBinaryPackages repo release =
+    mapM (binaryPackagesOfIndex repo release) (binaryIndexList release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Write a set of packages into a package index.
+putPackages :: LocalRepository -> Release -> PackageIndexLocal ->  [BinaryPackageLocal] -> IO ()
+putPackages repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
diff --git a/testdata/merge3-expected/Debian/Repo/PackageIndex.hs b/testdata/merge3-expected/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,87 @@
+module Debian.Repo.PackageIndex
+    ( packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPathList
+    , packageIndexDirList
+    , packageIndexList
+    , sourceIndexList
+    , binaryIndexList
+    , releaseDir
+    , showIndexBrief
+    , debSourceFromIndex
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Types (PackageIndex(..), Release(..))
+import Debian.Repo.Types.Slice (repoURI)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (DebSource(..), SourceType(..))
+import System.FilePath ((</>))
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index ++ "/" ++ packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release ++ "/" ++ sectionName' (packageIndexComponent index) ++ "/source"
+      _ -> (releaseDir release ++ "/" ++
+            sectionName' (packageIndexComponent index) ++
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "/binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists/" ++ (releaseName' . releaseName $ release)
+
+packageIndexPathList :: Release -> [FilePath]
+packageIndexPathList release = map (packageIndexPath release) . packageIndexList $ release
+
+packageIndexDirList :: Release -> [FilePath]
+packageIndexDirList release = map (packageIndexDir release) . packageIndexList $ release
+
+packageIndexList :: Release -> [PackageIndex]
+packageIndexList release = sourceIndexList release ++ binaryIndexList release
+
+sourceIndexList :: Release -> [PackageIndex]
+sourceIndexList release =
+    map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexList :: Release -> [PackageIndex]
+binaryIndexList release =
+    concat . map componentIndexes $ (releaseComponents release)
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          map archIndex (filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+showIndexBrief :: (Repository, Release) -> PackageIndex -> String
+showIndexBrief release index =
+    (releaseName' . releaseName . snd $ release) </> sectionName' (packageIndexComponent index) </> showArch (packageIndexArch index)
+    where showArch Source = "source"
+          showArch All = "all"
+          showArch x@(Binary _ _) = "binary-" ++ show (prettyArch x)
+
+debSourceFromIndex :: (Repository, Release) -> PackageIndex -> DebSource
+debSourceFromIndex (repo, release) index =
+    DebSource {sourceType = typ,
+               sourceUri = repoURI repo,
+               sourceDist = Right (dist, components)}
+    where
+      typ = case arch of (Binary _ _) -> Deb; Source -> DebSrc; All -> Deb
+      arch = packageIndexArch index
+      dist = releaseName $ release
+      components = releaseComponents $ release
diff --git a/testdata/merge3-expected/Debian/Repo/Slice.hs b/testdata/merge3-expected/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Slice.hs
@@ -0,0 +1,132 @@
+{-# LANGUAGE PackageImports, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , verifySourceLine
+    , verifySourcesList
+    , repoSources
+    , parseNamedSliceList
+    , parseNamedSliceList'
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName)
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.SourcesList (parseSourceLine, parseSourcesList)
+import Debian.Repo.Types (EnvPath(..), EnvRoot(..))
+import Debian.Repo.Types.Slice (repoKey, RepoKey(..))
+import Debian.Repo.Types.Repository (prepareRepository)
+import Debian.Repo.Types.Slice
+       (NamedSliceList(..), Slice(..), SliceList(..))
+import Debian.Sources (DebSource(..), SliceName(SliceName), SourceType(..))
+import Debian.URI (dirFromURI, fileFromURI, toURI')
+import Network.URI (URI(uriScheme, uriPath))
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+-- |Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadApt m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+parseNamedSliceList :: MonadApt m => (String, String) -> m (Maybe NamedSliceList)
+parseNamedSliceList (name, text) =
+    (verifySourcesList Nothing . parseSourcesList) text >>=
+    \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+-- |Create ReleaseCache info from an entry in the config file, which
+-- includes a dist name and the lines of the sources.list file.
+-- This also creates the basic 
+parseNamedSliceList' :: MonadApt m => (String, String) -> m NamedSliceList
+parseNamedSliceList' (name, text) =
+    do sources <- (verifySourcesList Nothing . parseSourcesList) text
+       return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+verifySourcesList :: MonadApt m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifySourceLine :: MonadApt m => Maybe EnvRoot -> String -> m Slice
+verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str)
+
+verifyDebSource :: MonadApt m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    repo >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      repo =
+          case uriScheme (sourceUri line) of
+            "file:" -> prepareRepository (Local (EnvPath chroot' (uriPath (sourceUri line))))
+            _ -> prepareRepository (Remote (toURI' (sourceUri line)))
+      chroot' = fromMaybe (EnvRoot "") chroot
diff --git a/testdata/merge3-expected/Debian/Repo/SourcesList.hs b/testdata/merge3-expected/Debian/Repo/SourcesList.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/SourcesList.hs
@@ -0,0 +1,142 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+--- | A DebSource represents a release of a remote repository and a
+--- method for accessing that repository.
+module Debian.Repo.SourcesList
+    (parseSourceLine,   -- String -> DebSource
+     parseSourceLine',  -- String -> Maybe DebSource
+     parseSourcesList,  -- String -> [DebSource]
+     quoteWords -- String -> [String]
+    )
+    where
+
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Sources (DebSource(..), SourceType(..))
+import Network.URI (parseURI, unEscapeString)
+
+{-
+
+deb uri distribution [component1] [componenent2] [...]
+
+The URI for the deb type must specify the base of the Debian
+distribution, from which APT will find the information it needs.
+
+distribution can specify an exact path, in which case the components
+must be omitted and distribution must end with a slash (/).
+
+If distribution does not specify an exact path, at least one component
+must be present.
+
+Distribution may also contain a variable, $(ARCH), which expands to
+the Debian architecture (i386, m68k, powerpc, ...)  used on the
+system.
+
+The rest of the line can be marked as a comment by using a #.
+
+Additional Notes:
+
+ + Lines can begin with leading white space.
+
+ + If the dist ends with slash (/), then it must be an absolute path
+   and it is an error to specify components after it.
+
+-}
+
+-- |quoteWords - similar to words, but with special handling of
+-- double-quotes and brackets.
+--
+-- The handling double quotes and [] is supposed to match:
+-- apt-0.6.44.2\/apt-pkg\/contrib\/strutl.cc:ParseQuoteWord()
+--
+-- The behaviour can be defined as:
+--
+--  Break the string into space seperated words ignoring spaces that
+--  appear between \"\" or []. Strip trailing and leading white space
+--  around words. Strip out double quotes, but leave the square
+--  brackets intact.
+quoteWords :: String -> [String]
+quoteWords [] = []
+quoteWords s = quoteWords' (dropWhile (==' ') s)
+    where
+      quoteWords' :: String -> [String]
+      quoteWords' [] = []
+      quoteWords' str =
+          case break (flip elem " [\"") str of
+            ([],[]) -> []
+            (w, []) -> [w]
+            (w, (' ':rest)) -> w : (quoteWords' (dropWhile (==' ') rest))
+            (w, ('"':rest)) ->
+                case break (== '"') rest of
+                  (w',('"':rest)) ->
+                      case quoteWords' rest of
+                        [] ->  [w ++ w']
+                        (w'':ws) -> ((w ++ w' ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing \" in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            (w, ('[':rest)) ->
+                case break (== ']') rest of
+                  (w',(']':rest)) ->
+                      case quoteWords' rest of
+                        []       -> [w ++ "[" ++ w' ++ "]"]
+                        (w'':ws) -> ((w ++ "[" ++ w' ++ "]" ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing ] in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            _ -> error ("the impossible happened in SourcesList.quoteWords")
+
+stripLine :: String -> String
+stripLine = takeWhile (/= '#') . dropWhile (== ' ')
+
+sourceLines :: String -> [String]
+sourceLines = filter (not . null) . map stripLine . lines
+
+-- |parseSourceLine -- parses a source line
+-- the argument must be a non-empty, valid source line with comments stripped
+-- see: 'sourceLines'
+parseSourceLine :: String -> DebSource
+parseSourceLine str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Deb
+                          "deb-src" -> DebSrc
+                          o -> error ("parseSourceLine: invalid type " ++ o ++ " in line:\n" ++ str)
+              theUri = case parseURI theUriStr of
+                         Nothing -> error ("parseSourceLine: invalid uri " ++ theUriStr ++ " in the line:\n" ++ str)
+                         Just u -> u
+              theDist = unEscapeString theDistStr
+          in
+            case last theDist of
+              '/' -> if null sections
+                      then DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              _ -> if null sections
+                    then error ("parseSourceLine: Dist is not an exact path, so at least one section is required on the line:\n" ++ str)
+                    else DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Right (parseReleaseName theDist, sections) }
+      _ -> error ("parseSourceLine: invalid line in sources.list:\n" ++ str)
+
+parseSourceLine' :: String -> Maybe DebSource
+parseSourceLine' str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Just Deb
+                          "deb-src" -> Just DebSrc
+                          _ -> Nothing
+              theUri = case parseURI theUriStr of
+                         Nothing -> Nothing
+                         Just u -> Just u
+              theDist = unEscapeString theDistStr
+          in
+            case (last theDist, theType, theUri) of
+              ('/', Just typ, Just uri) -> if null sections
+                      then Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              (_, Just typ, Just uri) -> if null sections
+                    then Nothing
+                    else Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Right ((parseReleaseName theDist), sections) }
+              _ -> Nothing
+      _ -> Nothing
+
+parseSourcesList :: String -> [DebSource]
+parseSourcesList = map parseSourceLine . sourceLines
diff --git a/testdata/merge3-expected/Debian/Repo/Sync.hs b/testdata/merge3-expected/Debian/Repo/Sync.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Sync.hs
@@ -0,0 +1,52 @@
+-- Comment above module head
+module Debian.Repo.Sync {-# WARNING "this is a warning" #-}
+    ( rsync
+    , foo
+    -- Comment after last export
+    ) where
+
+-- Comment before first import
+
+import Control.Monad.Trans (MonadIO)
+import qualified Data.ByteString as B (empty)
+import System.Exit (ExitCode)
+import System.FilePath (dropTrailingPathSeparator)
+-- Comment between two imporrts
+import System.Process (proc)
+import System.Process.Progress (keepResult, runProcessF)
+
+-- Comment before first decl
+rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode
+rsync extra source dest =
+    do result <- runProcessF (proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                                            [dropTrailingPathSeparator source ++ "/",
+                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult
+       case result of
+         [x] -> return x
+         _ -> error "Missing or multiple exit codes"
+
+-- Comment between two decls
+foo :: Int
+foo = 1
+
+{-
+handleExit 1 = "Syntax or usage error"
+handleExit 2 = "Protocol incompatibility"
+handleExit 3 = "Errors selecting input/output files, dirs"
+handleExit 4 = "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+handleExit 5 = "Error starting client-server protocol"
+handleExit 6 = "Daemon unable to append to log-file"
+handleExit 10 = "Error in socket I/O"
+handleExit 11 = "Error in file I/O"
+handleExit 12 = "Error in rsync protocol data stream"
+handleExit 13 = "Errors with program diagnostics"
+handleExit 14 = "Error in IPC code"
+handleExit 20 = "Received SIGUSR1 or SIGINT"
+handleExit 21 = "Some error returned by waitpid()"
+handleExit 22 = "Error allocating core memory buffers"
+handleExit 23 = "Partial transfer due to error"
+handleExit 24 = "Partial transfer due to vanished source files"
+handleExit 25 = "The --max-delete limit stopped deletions"
+handleExit 30 = "Timeout in data send/receive"
+handleExit 35 = "Timeout waiting for daemon connection"
+-}
diff --git a/testdata/merge3-expected/Debian/Repo/Types.hs b/testdata/merge3-expected/Debian/Repo/Types.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types.hs
@@ -0,0 +1,11 @@
+module Debian.Repo.Types
+    ( module Debian.Repo.Types.AptImage
+    , module Debian.Repo.Types.Slice
+    , module Debian.Repo.Types.PackageIndex
+    , module Debian.Repo.Types.Release
+    ) where
+
+import Debian.Repo.Types.AptImage
+import Debian.Repo.Types.Slice
+import Debian.Repo.Types.PackageIndex
+import Debian.Repo.Types.Release
diff --git a/testdata/merge3-expected/Debian/Repo/Types/AptBuildCache.hs b/testdata/merge3-expected/Debian/Repo/Types/AptBuildCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/AptBuildCache.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptBuildCache
+    ( AptBuildCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache)
+import Debian.Repo.Types.Slice (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+-- | An apt cache with extra sources.list lines for a local upload repository.
+class AptCache t => AptBuildCache t where
+    -- | The sources.list
+    aptSliceList :: t -> SliceList
diff --git a/testdata/merge3-expected/Debian/Repo/Types/AptCache.hs b/testdata/merge3-expected/Debian/Repo/Types/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/AptCache.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptCache
+    ( AptCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.Slice (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+class (Ord t, Eq t, Show t) => AptCache t where
+    globalCacheDir :: t -> FilePath
+    -- | The directory you might chroot to.
+    rootDir :: t -> EnvRoot
+    -- | The sources.list without the local repository
+    aptBaseSliceList :: t -> SliceList
+    -- | The build architecture
+    aptArch :: t -> Arch
+    -- | Return the all source packages in this AptCache.
+    aptSourcePackages :: t -> [SourcePackage]
+    -- | Return the all binary packages for the architecture of this AptCache.
+    aptBinaryPackages :: t -> [BinaryPackage]
+    -- | Name of release
+    aptReleaseName :: t -> ReleaseName
diff --git a/testdata/merge3-expected/Debian/Repo/Types/AptImage.hs b/testdata/merge3-expected/Debian/Repo/Types/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/AptImage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptImage
+    ( AptImage(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache(..))
+import Debian.Repo.Types.Slice (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import qualified Debian.Repo.Types.Slice as T (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+data AptImage =
+    AptImage { aptGlobalCacheDir :: FilePath
+             , aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSliceList :: T.SliceList
+             , aptImageReleaseName :: ReleaseName
+             , aptImageSourcePackages :: [SourcePackage]
+             , aptImageBinaryPackages :: [BinaryPackage]
+             }
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (aptImageReleaseName apt)
+
+instance AptCache AptImage where
+    globalCacheDir = aptGlobalCacheDir
+    rootDir = aptImageRoot
+    aptArch = aptImageArch
+    aptBaseSliceList = aptImageSliceList
+    aptSourcePackages = aptImageSourcePackages
+    aptBinaryPackages = aptImageBinaryPackages
+    aptReleaseName = aptImageReleaseName
+
+instance Ord AptImage where
+    compare a b = compare (aptImageReleaseName a) (aptImageReleaseName b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
diff --git a/testdata/merge3-expected/Debian/Repo/Types/PackageIndex.hs b/testdata/merge3-expected/Debian/Repo/Types/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/PackageIndex.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.PackageIndex
+    ( PackageIndex(..)
+    , PackageIndexLocal
+    , PackageID(packageVersion)
+    , prettyPackageID
+    , BinaryPackage(..)
+    , binaryPackageName
+    , prettyBinaryPackage
+    , makeBinaryPackageID
+    , SourcePackage(..)
+    , sourcePackageName
+    , makeSourcePackageID
+    , SourceControl(..)
+    , SourceFileSpec(..)
+    , PackageIDLocal
+    , BinaryPackageLocal
+    , SourcePackageLocal
+    , PackageVersion(..)
+    , PkgVersion(..)
+    , prettyPkgVersion
+    ) where
+
+import Data.Text (Text, map)
+import Debian.Arch (Arch(..))
+import qualified Debian.Control.Text as T (Paragraph)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName, Relations)
+import Debian.Release (Section(..))
+import Debian.Repo.Orphans ({- instances -})
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)
+
+class (Eq a, Ord a) => PackageVersion a where
+    pkgName :: a -> BinPkgName
+    pkgVersion :: a -> DebianVersion
+
+-- |This is an old type which is still used to interface with the
+-- Debian.Relation module.
+data PkgVersion = PkgVersion { getName :: BinPkgName
+                             , getVersion :: DebianVersion
+                             } deriving (Eq, Ord, Show)
+
+instance PackageVersion PkgVersion where
+    pkgName = getName
+    pkgVersion = getVersion
+
+prettyPkgVersion :: PkgVersion -> Doc
+prettyPkgVersion v = pretty (getName v) <> text "=" <> prettyDebianVersion (getVersion v)
+
+---------------- PACKAGES AND PACKAGE INDEXES -------------
+
+-- |The PackageIndex type represents a file containing control
+-- information about debian packages, either source or binary.
+-- Though the control information for a binary package does not
+-- specify an architecture, the architecture here is that of
+-- the environment where the package information is cached.
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+type PackageIndexLocal = PackageIndex
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage p = pretty (pkgName p) <> text "-" <> prettyDebianVersion (pkgVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
+
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+
+-- | The 'PackageID' type fully identifies a package by name, version,
+-- and a 'PackageIndex' which identifies the package's release,
+-- component and architecture.
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+binaryPackageName :: BinaryPackage -> BinPkgName
+binaryPackageName = packageName . packageID
+
+sourcePackageName :: SourcePackage -> SrcPkgName
+sourcePackageName = packageName . sourcePackageID
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = pretty (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      }
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+type PackageIDLocal n = PackageID n
+type BinaryPackageLocal = BinaryPackage
+type SourcePackageLocal = SourcePackage
diff --git a/testdata/merge3-expected/Debian/Repo/Types/Release.hs b/testdata/merge3-expected/Debian/Repo/Types/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/Release.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , makeReleaseInfo
+    , parseComponents
+    , parseArchitectures
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure))
+import Data.Text (Text, unpack)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (fieldValue, Paragraph)
+import Debian.Release (parseSection', ReleaseName(..), Section(..))
+import Text.Regex (mkRegex, splitRegex)
+import qualified Tmp.File as F (File(..))
+
+-- FIXME: The lists here should be sets so that == and compare work properly.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: [Arch]
+                       , releaseComponents :: [Section]
+                       } deriving (Eq, Ord, Read, Show)
+
+makeReleaseInfo :: F.File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(F.File {F.text = Failure msgs}) _name _aliases =
+    error $ "Failure reading " ++ show (F.path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(F.File {F.text = Success info}) name aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = name
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (F.path file)
+
+parseArchitectures :: Text -> [Arch]
+parseArchitectures archList =
+    map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
diff --git a/testdata/merge3-expected/Debian/Repo/Types/Repository.hs b/testdata/merge3-expected/Debian/Repo/Types/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/Repository.hs
@@ -0,0 +1,431 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repository
+    ( Repository
+    , LocalRepository, repoRoot, repoLayout, repoReleaseInfoLocal
+    , RemoteRepository
+    , Layout(..)
+    , MonadRepoCache(getRepoCache, putRepoCache)
+    , loadRepoCache
+    , saveRepoCache
+    , fromLocalRepository
+    , readLocalRepo
+    , prepareLocalRepository
+    , copyLocalRepo -- repoCD
+    , prepareRepository
+    , setRepositoryCompatibility
+    , flushLocalRepository
+    , poolDir'
+    -- , sliceReleaseNames
+    , repoReleaseNames
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure), maybeRead)
+import Control.Exception (ErrorCall(..), SomeException, toException)
+import Control.Monad (filterM, unless, when)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IO (catch)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.List (groupBy, intercalate, isPrefixOf, partition, sort)
+import Data.Map as Map (empty, fromList, insert, insertWith, lookup, Map, toList, union)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (Text, unpack)
+import Debian.Changes (ChangedFileSpec(changedFileSection), ChangesFile(changeInfo))
+import qualified Debian.Control.Text as T (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph, Paragraph')
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section, sectionName', SubSection(section))
+import Debian.Repo.Monads.Top (MonadTop, sub)
+import Debian.Repo.Sync (rsync)
+import Debian.Repo.Types.Slice
+       (EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Types.Release (makeReleaseInfo, Release(releaseName))
+import Debian.Repo.Types.Slice
+       (compatibilityFile, libraryCompatibilityLevel, Repo(..),
+        RepoKey(..))
+import Debian.URI (dirFromURI, fileFromURI, fromURI', toURI', URI(uriScheme, uriPath), URI', uriToString')
+import Debian.UTF8 as Deb (decode)
+import Extra.Files (maybeWriteFile)
+import Extra.List (partitionM)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink, setFileMode)
+import System.Process.Progress (qPutStr, qPutStrLn, quieter)
+import System.Unix.Directory (removeRecursiveSafely)
+import qualified Text.Format as F (Pretty(..))
+import Text.PrettyPrint.ANSI.Leijen (text)
+import Text.Regex (matchRegex, mkRegex)
+import qualified Tmp.File as F (File(..), readFile, Source(RemotePath))
+
+--------------------- REPOSITORY -----------------------
+
+-- | The Repository type reprents any instance of the Repo class, so
+-- it might be local or remote.
+--data Repository = forall a. (Repo a) => Repository a
+data Repository
+    = LocalRepo LocalRepository
+    | RemoteRepo RemoteRepository
+    deriving (Show, Read)
+
+instance Ord Repository where
+    compare a b = compare (repoKey a) (repoKey b)
+
+instance Eq Repository where
+    a == b = compare a b == EQ
+
+-- | URI has a bogus show function, which we are using here.
+instance F.Pretty URI' where
+    pretty = text . show . fromURI'
+
+instance F.Pretty Repository where
+    pretty (LocalRepo r) = text $ outsidePath (repoRoot r)
+    pretty (RemoteRepo r) = F.pretty r
+
+instance Repo Repository where
+    repoKey (LocalRepo (LocalRepository path _ _)) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoKey (RemoteRepo (RemoteRepository uri _)) = Remote uri
+    repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
+    repoReleaseInfo (RemoteRepo (RemoteRepository _ info)) = info
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot_ :: EnvPath
+      , repoLayout_ :: (Maybe Layout)
+      , repoReleaseInfoLocal_ :: [Release]
+      } deriving (Read, Show)
+
+instance Ord LocalRepository where
+    compare (LocalRepository a _ _) (LocalRepository b _ _) = compare a b
+
+instance Eq LocalRepository where
+    a == b = compare a b == EQ
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show)
+
+instance Ord RemoteRepository where
+    compare (RemoteRepository a _) (RemoteRepository b _) = compare a b
+
+instance Eq RemoteRepository where
+    a == b = compare a b == EQ
+
+instance F.Pretty RemoteRepository where
+    pretty (RemoteRepository s _) = F.pretty s
+
+repoRoot :: LocalRepository -> EnvPath
+repoRoot = repoRoot_
+
+repoLayout :: LocalRepository -> Maybe Layout
+repoLayout = repoLayout_
+
+repoReleaseInfoLocal :: LocalRepository -> [Release]
+repoReleaseInfoLocal = repoReleaseInfoLocal_
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+class MonadIO m => MonadRepoCache m where
+    getRepoCache :: m (Map RepoKey Repository)
+    putRepoCache :: Map RepoKey Repository -> m ()
+
+modifyRepoCache :: MonadRepoCache m => (Map RepoKey Repository -> Map RepoKey Repository) -> m ()
+modifyRepoCache f = do
+    s <- getRepoCache
+    putRepoCache (f s)
+
+fromLocalRepository :: LocalRepository -> Repository
+fromLocalRepository = LocalRepo
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: (MonadRepoCache m, MonadTop m) => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty)) >>= putRepoCache
+    where
+      loadRepoCache' :: FilePath -> IO (Map RepoKey Repository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: (MonadIO m, MonadTop m, MonadRepoCache m) => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- getRepoCache
+             repoCache <- liftIO $ loadCache path
+             let merged = show . Map.toList $ Map.union live repoCache
+             liftIO (F.removeLink path `IO.catch` (\e -> unless (isDoesNotExistError e) (ioError e))) >> liftIO (writeFile path merged)
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map RepoKey Repository)
+            loadCache path =
+                readFile path `IO.catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+readLocalRepo :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+readLocalRepo root layout =
+    do state <- getRepoCache
+       case Map.lookup (Local root) state of
+         Just (RemoteRepo (RemoteRepository _ _)) -> error "readLocalRepo: internal error" -- somehow a remote repo got associated with a Local RepoKey
+         Just (LocalRepo repo) -> return repo
+         Nothing ->
+             do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+                (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+                linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+                let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+                let distGroups = groupBy fstEq . sort $ aliasPairs
+                let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+                releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+                qPutStrLn ("LocalRepository releaseInfo " ++ show root ++ ": " ++ show releaseInfo)
+                let repo = LocalRepository { repoRoot_ = root
+                                           , repoLayout_ = layout
+                                           , repoReleaseInfoLocal_ = releaseInfo }
+                putRepoCache (insertWith (\ _ x -> x) (Local root) (LocalRepo repo) state)
+                return repo
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist ++ "/Release"
+      distDir = outsidePath root ++ "/dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path dist aliases =
+    liftIO (F.readFile path) >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> F.File Text -> Release
+parseRelease name aliases file =
+    case F.text file of
+      Failure msgs -> error $ "Could not read " ++ show (F.path file) ++ ": " ++ show msgs
+      Success text ->
+          case T.parseControl (show (F.path file)) text of
+            Left msg -> error $ "Failure parsing " ++ show (F.path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (F.path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (F.File {F.path = F.path file, F.text = Success info}) name aliases
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository root layout =
+    do mapM_ (liftIO . initDir)
+                 [(".", 0o40755),
+                  ("dists", 0o40755),
+                  ("incoming", 0o41755),
+                  ("removed", 0o40750),
+                  ("reject", 0o40750)]
+       layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+                  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+       mapM_ (liftIO . initDir)
+                 (case layout' of
+                    Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+                    Just Flat -> []
+                    Nothing -> [])
+       readLocalRepo root layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+{-      notSymbolicLink root name =
+          getSymbolicLinkStatus (root ++ "/dists/" ++ name) >>= return . not . isSymbolicLink
+      hasReleaseFile root name =
+          doesFileExist (root ++ "/dists/" ++ name ++ "/Release") -}
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadRepoCache m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do qPutStrLn ("Syncing local repository from " ++ src ++ " -> " ++ dst)
+       liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       result <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         ExitSuccess ->
+             do let repo' = repo {repoRoot_ = dest}
+                modifyRepoCache (Map.insert (Local dest) (LocalRepo repo'))
+                return repo'
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path text
+    where text = show libraryCompatibilityLevel ++ "\n"
+          path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepoCache m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       prepareLocalRepository (repoRoot r) (repoLayout r)
+
+prepareRepository :: MonadRepoCache m => RepoKey -> m Repository
+prepareRepository key =
+    case key of
+      Local path -> prepareLocalRepository path Nothing >>= return . LocalRepo
+      Remote uri -> prepareRepository' (fromURI' uri)
+
+-- | Prepare a repository, which may be remote or local depending on
+-- the URI.
+prepareRepository' :: MonadRepoCache m => URI -> m Repository
+prepareRepository' uri =
+    do state <- getRepoCache
+       repo <- maybe newRepo return (Map.lookup (Remote (toURI' uri)) state)
+       putRepoCache (Map.insert (Remote (toURI' uri)) repo state)
+       return repo
+    where
+      newRepo =
+             case uriScheme uri of
+               "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= return . LocalRepo
+               -- FIXME: We only want to verifyRepository on demand.
+               _ -> verifyRepository (toURI' uri)
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+{-# NOINLINE verifyRepository #-}
+verifyRepository :: MonadRepoCache m => URI' -> m Repository
+verifyRepository uri =
+    do state <- getRepoCache
+       case Map.lookup (Remote uri) state of
+         Just repo -> return repo
+         Nothing ->
+             do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+                let repo = RemoteRepo (RemoteRepository uri releaseInfo)
+                modifyRepoCache (Map.insert (Remote uri) repo)
+                return repo
+
+-- Nice code to do caching, but I figured out how to fix the old code.
+
+--instance Read URI where
+--    readsPrec _ s = [(fromJust (parseURI s), "")]
+--
+---- |Get the list of releases of a remote repository.
+--getReleaseInfo :: FilePath
+--               -> Bool     -- ^ If False don't look at existing cache
+--               -> URI
+--               -> IO [ReleaseInfo]
+--getReleaseInfo top tryCache uri =
+--    readCache >>= \ cache ->
+--    return (lookup uri cache) >>=
+--    maybe (updateCache cache) return
+--    where
+--      cachePath = top ++ "/repoCache"
+--      readCache :: IO [(URI, [ReleaseInfo])]
+--      readCache = if tryCache
+--                  then try (readFile cachePath >>= return . read) >>= return . either (\ (_ :: SomeException) -> []) id
+--                  else return []
+--      updateCache :: [(URI, [ReleaseInfo])] -> IO [ReleaseInfo]
+--      updateCache pairs = getReleaseInfoRemote uri >>= \ info ->
+--                          writeCache ((uri, info) : pairs) >> return info
+--      writeCache :: [(URI, [ReleaseInfo])] -> IO ()
+--      writeCache pairs = writeFile (show pairs) cachePath
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    quieter 2 (dirFromURI distsURI) >>=
+    quieter 2 . either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [F.File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (F.File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: F.File (T.Paragraph' Text) -> Maybe Text
+      getSuite (F.File {F.text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (F.File {F.text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (F.File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ F.File {F.path = F.RemotePath releaseURI, F.text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
+
+repoReleaseNames :: Repository -> [ReleaseName]
+repoReleaseNames (RemoteRepo (RemoteRepository _ rels)) = map releaseName rels
+repoReleaseNames (LocalRepo (LocalRepository _ _ rels)) = map releaseName rels
diff --git a/testdata/merge3-expected/Debian/Repo/Types/Slice.hs b/testdata/merge3-expected/Debian/Repo/Types/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Debian/Repo/Types/Slice.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Slice
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    , Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    , EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+import Debian.Sources (DebSource(..), SliceName(..), SourceType(..))
+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), vcat)
+
+
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Text (unpack)
+import Debian.Repo.Types.Release (Release)
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+
+
+ 
+data Slice = Slice{sliceRepoKey :: Debian.Repo.Types.Slice.RepoKey,
+                   sliceSource :: DebSource}
+           deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty SliceList where
+    pretty = vcat . map (pretty . sliceSource) . slices
+
+deriving instance Show SourceType
+deriving instance Show DebSource
+
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
+
+
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
+
diff --git a/testdata/merge3-expected/Text/Format.hs b/testdata/merge3-expected/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/merge3-expected/Tmp/File.hs b/testdata/merge3-expected/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge3-expected/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
diff --git a/testdata/merge4-expected/M1.hs b/testdata/merge4-expected/M1.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge4-expected/M1.hs
@@ -0,0 +1,10 @@
+module M1
+    ( c
+    , module Out
+    , module Out
+    ) where
+
+import Out (a)
+import Out as Foo (b)
+
+c = Out.a + Foo.b
diff --git a/testdata/merge4-expected/Out.hs b/testdata/merge4-expected/Out.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge4-expected/Out.hs
@@ -0,0 +1,12 @@
+module Out
+    ( a
+    , b
+    ) where
+
+
+
+a = 1
+
+
+b = 2 + Out.a
+
diff --git a/testdata/merge4/In1.hs b/testdata/merge4/In1.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge4/In1.hs
@@ -0,0 +1,5 @@
+module In1
+    ( a
+    ) where
+
+a = 1
diff --git a/testdata/merge4/In2.hs b/testdata/merge4/In2.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge4/In2.hs
@@ -0,0 +1,8 @@
+module In2
+    ( b
+    , module In1
+    ) where
+
+import In1 as Foo
+
+b = 2 + Foo.a
diff --git a/testdata/merge4/M1.hs b/testdata/merge4/M1.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge4/M1.hs
@@ -0,0 +1,10 @@
+module M1
+    ( c
+    , module In1
+    , module In2
+    ) where
+
+import In1 (a)
+import In2 as Foo (b)
+
+c = In1.a + Foo.b
diff --git a/testdata/merge5-expected/Apt.hs b/testdata/merge5-expected/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5-expected/Apt.hs
@@ -0,0 +1,106 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt
+    ( AptIO
+    , AptIOT
+    , AptState(AptState, aptImageMap, binaryPackageMap, releaseMap,
+         repoMap, sourcePackageMap)
+    , initState
+    , MonadApt(getApt, putApt)
+    ) where
+
+
+
+
+import Control.Monad.State (StateT)
+
+
+
+import qualified Data.Map as Map (Map)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (SliceName)
+import System.Posix.Files (FileStatus)
+
+
+
+import qualified Data.Map as Map (empty)
+
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, MonadIO, MonadTrans(lift), put)
+import Debian.Repo.Types.Repository (MonadRepoCache(..))
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+
+
+
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.State (MonadIO)
+
+type AptIO = AptIOT IO
+
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository		-- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage	-- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage])
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
+
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
diff --git a/testdata/merge5/Apt/AptIO.hs b/testdata/merge5/Apt/AptIO.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/AptIO.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.AptIO
+    ( AptIO
+    ) where
+
+import Apt.AptIOT (AptIOT)
+type AptIO = AptIOT IO
diff --git a/testdata/merge5/Apt/AptIOT.hs b/testdata/merge5/Apt/AptIOT.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/AptIOT.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.AptIOT
+    ( AptIOT
+    ) where
+
+import Control.Monad.State (StateT)
+import Apt.AptState (AptState)
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
diff --git a/testdata/merge5/Apt/AptState.hs b/testdata/merge5/Apt/AptState.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/AptState.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.AptState
+    ( AptState(AptState, aptImageMap, binaryPackageMap, releaseMap,
+         repoMap, sourcePackageMap)
+    ) where
+
+import qualified Data.Map as Map (Map)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (SliceName)
+import System.Posix.Files (FileStatus)
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository		-- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage	-- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage])
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
diff --git a/testdata/merge5/Apt/InitState.hs b/testdata/merge5/Apt/InitState.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/InitState.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.InitState
+    ( initState
+    ) where
+
+import qualified Data.Map as Map (empty)
+import Apt.AptState (AptState(AptState, aptImageMap, binaryPackageMap, releaseMap, repoMap, sourcePackageMap))
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
diff --git a/testdata/merge5/Apt/Instances.hs b/testdata/merge5/Apt/Instances.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/Instances.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.Instances
+    ( 
+    ) where
+
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, MonadIO, MonadTrans(lift), put)
+import Apt.AptIOT (AptIOT)
+import Apt.AptState (repoMap)
+import Apt.MonadApt (MonadApt(getApt, putApt))
+import Debian.Repo.Types.Repository (MonadRepoCache(..))
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/merge5/Apt/MonadApt.hs b/testdata/merge5/Apt/MonadApt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge5/Apt/MonadApt.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Apt.MonadApt
+    ( MonadApt(getApt, putApt)
+    ) where
+
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.State (MonadIO)
+import Apt.AptState (AptState)
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
diff --git a/testdata/merge6-expected/A.hs b/testdata/merge6-expected/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6-expected/A.hs
@@ -0,0 +1,11 @@
+module A where
+
+
+
+
+
+c = 1
+
+a = 1
+b = 2
+
diff --git a/testdata/merge6-expected/B/D.hs b/testdata/merge6-expected/B/D.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6-expected/B/D.hs
@@ -0,0 +1,5 @@
+module B.D 
+    ( d
+    ) where
+
+d = 2
diff --git a/testdata/merge6-expected/Test.hs b/testdata/merge6-expected/Test.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6-expected/Test.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import A (a, b)
+import B.D (d)
+
+main = putStrLn $ "Hello, world. a=" ++ show a ++ ", b=" ++ show b ++ ", d=" ++ show d
diff --git a/testdata/merge6/A.hs b/testdata/merge6/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6/A.hs
@@ -0,0 +1,4 @@
+module A where
+
+a = 1
+b = 2
diff --git a/testdata/merge6/B/C.hs b/testdata/merge6/B/C.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6/B/C.hs
@@ -0,0 +1,5 @@
+module B.C 
+    ( c
+    ) where
+
+c = 1
diff --git a/testdata/merge6/B/D.hs b/testdata/merge6/B/D.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6/B/D.hs
@@ -0,0 +1,5 @@
+module B.D 
+    ( d
+    ) where
+
+d = 2
diff --git a/testdata/merge6/Test.hs b/testdata/merge6/Test.hs
new file mode 100644
--- /dev/null
+++ b/testdata/merge6/Test.hs
@@ -0,0 +1,7 @@
+module Main where
+
+import A (a, b)
+import B.C (c)
+import B.D (d)
+
+main = putStrLn $ "Hello, world. a=" ++ show a ++ ", b=" ++ show b ++ ", d=" ++ show d
diff --git a/testdata/split-expected/Data/Boolean.hs b/testdata/split-expected/Data/Boolean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Boolean.hs
@@ -0,0 +1,157 @@
+{-# OPTIONS -fno-warn-incomplete-patterns #-}
+-- |
+-- Module      : Data.Boolean
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This library provides a representation of boolean formulas that is
+-- used by the solver in "Data.Boolean.SatSolver".
+-- 
+-- We also define a function to simplify formulas, a type for
+-- conjunctive normalforms, and a function that creates them from
+-- boolean formulas.
+-- 
+module Data.Boolean ( 
+
+  Boolean(..), 
+
+  Literal(..), literalVar, invLiteral, isPositiveLiteral, 
+
+  CNF, Clause, booleanToCNF
+
+  ) where
+
+import Data.Maybe ( mapMaybe )
+import qualified Data.IntMap as IM
+
+import Control.Monad ( guard, liftM )
+
+-- | Boolean formulas are represented as values of type @Boolean@.
+-- 
+data Boolean
+  -- | Variables are labeled with an @Int@,
+  = Var Int
+  -- | @Yes@ represents /true/,
+  | Yes
+  -- | @No@ represents /false/,
+  | No
+  -- | @Not@ constructs negated formulas,
+  | Not Boolean
+  -- | and finally we provide conjunction
+  | Boolean :&&: Boolean
+  -- | and disjunction of boolean formulas.
+  | Boolean :||: Boolean
+ deriving Show
+
+-- | Literals are variables that occur either positively or negatively.
+-- 
+data Literal = Pos Int | Neg Int deriving (Eq, Show)
+
+-- | This function returns the name of the variable in a literal.
+-- 
+literalVar :: Literal -> Int
+literalVar (Pos n) = n
+literalVar (Neg n) = n
+
+-- | This function negates a literal.
+-- 
+invLiteral :: Literal -> Literal
+invLiteral (Pos n) = Neg n
+invLiteral (Neg n) = Pos n
+
+-- | This predicate checks whether the given literal is positive.
+-- 
+isPositiveLiteral :: Literal -> Bool
+isPositiveLiteral (Pos _) = True
+isPositiveLiteral _       = False
+
+-- | Conjunctive normalforms are lists of lists of literals.
+-- 
+type CNF     = [Clause]
+type Clause  = [Literal]
+
+-- | 
+-- We convert boolean formulas to conjunctive normal form by pushing
+-- negations down to variables and repeatedly applying the
+-- distributive laws.
+-- 
+booleanToCNF :: Boolean -> CNF
+booleanToCNF
+  = mapMaybe (simpleClause . map literal . disjunction)
+  . conjunction
+  . asLongAsPossible distribute
+  . asLongAsPossible pushNots
+  . asLongAsPossible elim
+ where
+  elim (Not Yes)      = Just No
+  elim (Not No)       = Just Yes
+  elim (No  :&&: _)   = Just No
+  elim (Yes :&&: x)   = Just x
+  elim (_   :&&: No)  = Just No
+  elim (x   :&&: Yes) = Just x 
+  elim (Yes :||: _)   = Just Yes
+  elim (No  :||: x)   = Just x
+  elim (_   :||: Yes) = Just Yes
+  elim (x   :||: No)  = Just x
+  elim _              = Nothing
+
+  pushNots (Not (Not x))  = Just x
+  pushNots (Not (x:&&:y)) = Just (Not x :||: Not y)
+  pushNots (Not (x:||:y)) = Just (Not x :&&: Not y)
+  pushNots _              = Nothing
+
+  distribute (x:||:(y:&&:z)) = Just ((x:||:y):&&:(x:||:z))
+  distribute ((x:&&:y):||:z) = Just ((x:||:z):&&:(y:||:z))
+  distribute _               = Nothing
+
+  literal (Var x)       = Pos x
+  literal (Not (Var x)) = Neg x
+
+
+-- private helper functions
+
+-- remove duplicate literals from clauses and drop clauses that
+-- contain one literal both positively and negatively.
+--
+simpleClause :: Clause -> Maybe Clause
+simpleClause = liftM (map lit . IM.toList) . foldl add (Just IM.empty)
+ where
+  lit (x,True)  = Pos x
+  lit (x,False) = Neg x
+
+  add mm l = do
+    m <- mm
+    let x = literalVar l; kind = isPositiveLiteral l
+    maybe (Just (IM.insert x kind m))
+          (\b -> guard (b==kind) >> Just m)
+          (IM.lookup x m)
+
+conjunction :: Boolean -> [Boolean]
+conjunction b = flat b []
+ where flat Yes      = id
+       flat (x:&&:y) = flat x . flat y
+       flat x        = (x:)
+
+disjunction :: Boolean -> [Boolean]
+disjunction b = flat b []
+ where flat No       = id
+       flat (x:||:y) = flat x . flat y
+       flat x        = (x:)
+
+asLongAsPossible :: (Boolean -> Maybe Boolean) -> Boolean -> Boolean
+asLongAsPossible f = everywhere g
+ where g x = maybe x (everywhere g) (f x)
+
+everywhere :: (Boolean -> Boolean) -> Boolean -> Boolean
+everywhere f = f . atChildren (everywhere f)
+
+atChildren :: (Boolean -> Boolean) -> Boolean -> Boolean
+atChildren f (Not x)  = Not (f x)
+atChildren f (x:&&:y) = f x :&&: f y
+atChildren f (x:||:y) = f x :||: f y
+atChildren _ x        = x
+
diff --git a/testdata/split-expected/Data/Boolean/SatSolver.hs b/testdata/split-expected/Data/Boolean/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Boolean/SatSolver.hs
@@ -0,0 +1,183 @@
+-- |
+-- Module      : Data.Boolean.SatSolver
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This Haskell library provides an implementation of the
+-- Davis-Putnam-Logemann-Loveland algorithm
+-- (cf. <http://en.wikipedia.org/wiki/DPLL_algorithm>) for the boolean
+-- satisfiability problem. It not only allows to solve boolean
+-- formulas in one go but also to add constraints and query bindings
+-- of variables incrementally.
+-- 
+-- The implementation is not sophisticated at all but uses the basic
+-- DPLL algorithm with unit propagation.
+-- 
+module Data.Boolean.SatSolver (
+
+  Boolean(..), SatSolver, Literal(..), literalVar, invLiteral, isPositiveLiteral, CNF, Clause, booleanToCNF,
+
+  newSatSolver, isSolved, 
+
+  lookupVar, assertTrue, assertTrue', branchOnVar, selectBranchVar, solve, isSolvable
+
+  ) where
+
+import Data.List
+import Data.Boolean
+
+import Control.Monad.Writer
+
+import qualified Data.IntMap as IM
+
+-- | A @SatSolver@ can be used to solve boolean formulas.
+-- 
+data SatSolver = SatSolver { clauses :: CNF, bindings :: IM.IntMap Bool }
+ deriving Show
+
+-- | A new SAT solver without stored constraints.
+-- 
+newSatSolver :: SatSolver
+newSatSolver = SatSolver [] IM.empty
+
+-- | This predicate tells whether all constraints are solved.
+-- 
+isSolved :: SatSolver -> Bool
+isSolved = null . clauses
+
+-- |
+-- We can lookup the binding of a variable according to the currently
+-- stored constraints. If the variable is unbound, the result is
+-- @Nothing@.
+-- 
+lookupVar :: Int -> SatSolver -> Maybe Bool
+lookupVar name = IM.lookup name . bindings
+
+-- | 
+-- We can assert boolean formulas to update a @SatSolver@. The
+-- assertion may fail if the resulting constraints are unsatisfiable.
+-- 
+assertTrue :: MonadPlus m => Boolean -> SatSolver -> m SatSolver
+assertTrue formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      (booleanToCNF formula)
+  simplify (solver { clauses = newClauses })
+
+assertTrue' :: MonadPlus m => CNF -> SatSolver -> m SatSolver
+assertTrue' formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      formula
+  simplify (solver { clauses = newClauses })
+
+-- |
+-- This function guesses a value for the given variable, if it is
+-- currently unbound. As this is a non-deterministic operation, the
+-- resulting solvers are returned in an instance of @MonadPlus@.
+-- 
+branchOnVar :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnVar name solver =
+  maybe (branchOnUnbound name solver)
+        (const (return solver))
+        (lookupVar name solver)
+
+-- |
+-- We select a variable from the shortest clause hoping to produce a
+-- unit clause.
+--
+selectBranchVar :: SatSolver -> Int
+selectBranchVar = literalVar . head . head . sortBy shorter . clauses
+
+-- | 
+-- This function guesses values for variables such that the stored
+-- constraints are satisfied. The result may be non-deterministic and
+-- is, hence, returned in an instance of @MonadPlus@.
+-- 
+solve :: MonadPlus m => SatSolver -> m SatSolver
+solve solver
+  | isSolved solver = return solver
+  | otherwise = branchOnUnbound (selectBranchVar solver) solver >>= solve
+
+-- |
+-- This predicate tells whether the stored constraints are
+-- solvable. Use with care! This might be an inefficient operation. It
+-- tries to find a solution using backtracking and returns @True@ if
+-- and only if that fails.
+-- 
+isSolvable :: SatSolver -> Bool
+isSolvable = not . null . solve
+
+
+-- private helper functions
+
+addClause :: MonadPlus m => IM.IntMap Bool -> m [Clause] -> Clause -> m [Clause]
+addClause binds mclauses newClause = do
+  oldClauses <- mclauses
+  let unboundLits = foldl (addUnbound binds) (Just []) newClause
+  maybe (return oldClauses)
+        (\lits -> guard (not (null lits)) >> return (lits:oldClauses))
+        unboundLits
+
+addUnbound :: IM.IntMap Bool -> Maybe Clause -> Literal -> Maybe Clause
+addUnbound binds mlits lit = do
+  lits <- mlits
+  maybe (Just (lit:lits))
+        (\b -> guard (b /= isPositiveLiteral lit) >> return lits)
+        (IM.lookup (literalVar lit) binds)
+
+updateSolver :: MonadPlus m => CNF -> [(Int,Bool)] -> SatSolver -> m SatSolver
+updateSolver cs bs solver = do
+  bs' <- foldr (uncurry insertBinding) (return (bindings solver)) bs
+  return $ solver { clauses = cs, bindings = bs' }
+
+insertBinding :: MonadPlus m
+              => Int -> Bool -> m (IM.IntMap Bool) -> m (IM.IntMap Bool)
+insertBinding name newValue binds = do
+  bs <- binds
+  maybe (return (IM.insert name newValue bs))
+        (\oldValue -> do guard (oldValue==newValue); return bs)
+        (IM.lookup name bs)
+
+simplify :: MonadPlus m => SatSolver -> m SatSolver
+simplify solver = do
+  (cs,bs) <- runWriterT . simplifyClauses . clauses $ solver
+  updateSolver cs bs solver
+
+simplifyClauses :: MonadPlus m => CNF -> WriterT [(Int,Bool)] m CNF
+simplifyClauses [] = return []
+simplifyClauses allClauses = do
+  let shortestClause = head . sortBy shorter $ allClauses
+  guard (not (null shortestClause))
+  if null (tail shortestClause)
+   then propagate (head shortestClause) allClauses >>= simplifyClauses
+   else return allClauses
+
+propagate :: MonadPlus m => Literal -> CNF -> WriterT [(Int,Bool)] m CNF
+propagate literal allClauses = do
+  tell [(literalVar literal, isPositiveLiteral literal)]
+  return (foldr prop [] allClauses)
+ where
+  prop c cs | literal `elem` c = cs
+            | otherwise        = filter (invLiteral literal/=) c : cs
+
+branchOnUnbound :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnUnbound name solver =
+  guess (Pos name) solver `mplus` guess (Neg name) solver
+
+guess :: MonadPlus m => Literal -> SatSolver -> m SatSolver
+guess literal solver = do
+  (cs,bs) <- runWriterT (propagate literal (clauses solver) >>= simplifyClauses)
+  updateSolver cs bs solver
+
+shorter :: [a] -> [a] -> Ordering
+shorter []     []     = EQ
+shorter []     _      = LT
+shorter _      []     = GT
+shorter (_:xs) (_:ys) = shorter xs ys
+
+
diff --git a/testdata/split-expected/Data/Logic/Classes/Apply.hs b/testdata/split-expected/Data/Logic/Classes/Apply.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Apply.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures #-}
+-- | The Apply class represents a type of atom the only supports predicate application.
+module Data.Logic.Classes.Apply
+    ( Apply(..)
+    , Predicate
+    , apply
+    , zipApplys
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , showApply
+    , prettyApply
+    , varApply
+    , substApply
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Term (Term, showTerm, prettyTerm, fvt, tsubst)
+import Data.List (intercalate, intersperse)
+import Data.Maybe (fromMaybe)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), text, empty, parens, cat)
+
+class (Arity p, Constants p, Eq p, Ord p, Data p, Pretty p) => Predicate p
+
+class Predicate p => Apply atom p term | atom -> p term where
+    foldApply :: (p -> [term] -> r) -> (Bool -> r) -> atom -> r
+    apply' :: p -> [term] -> atom
+
+-- | apply' with an arity check - clients should always call this.
+apply :: Apply atom p term => p -> [term] -> atom
+apply p ts =
+    case arity p of
+      Just n | n /= length ts -> error "arity"
+      _ -> apply' p ts
+
+zipApplys :: Apply atom p term =>
+            (p -> [term] -> p -> [term] -> Maybe r)
+         -> (Bool -> Bool -> Maybe r)
+         -> atom -> atom -> Maybe r
+zipApplys ap tf a1 a2 =
+    foldApply ap' tf' a1
+    where
+      ap' p1 ts1 = foldApply (ap p1 ts1) (\ _ -> Nothing) a2
+      tf' x1 = foldApply (\ _ _ -> Nothing) (tf x1) a2
+
+apply0 p = if fromMaybe 0 (arity p) == 0 then apply' p [] else error "arity"
+apply1 p a = if fromMaybe 1 (arity p) == 1 then apply' p [a] else error "arity"
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then apply' p [a,b] else error "arity"
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then apply' p [a,b,c] else error "arity"
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then apply' p [a,b,c,d] else error "arity"
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then apply' p [a,b,c,d,e] else error "arity"
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then apply' p [a,b,c,d,e,f] else error "arity"
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then apply' p [a,b,c,d,e,f,g] else error "arity"
+
+showApply :: (Apply atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showApply =
+    foldApply (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+              (\ x -> if x then "true" else "false")
+
+prettyApply :: (Apply atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyApply pv pp pf prec atom =
+    foldApply (\ p ts ->
+                   pp p <> case ts of
+                             [] -> empty
+                             _ -> parens (cat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+              (\ x -> text (if x then "true" else "false"))
+              atom
+
+-- | Return the variables that occur in an instance of Apply.
+varApply :: (Apply atom p term, Term term v f) => atom -> Set.Set v
+varApply = foldApply (\ _ args -> Set.unions (map fvt args)) (const Set.empty)
+
+substApply :: (Apply atom p term, Constants atom, Term term v f) => Map.Map v term -> atom -> atom
+substApply env = foldApply (\ p args -> apply p (map (tsubst env) args)) fromBool
+
+{-
+instance (Apply atom p term, Term term v f, Constants atom) => Formula atom term v where
+    allVariables = varApply
+    freeVariables = varApply
+    substitute = substApply
+-}
diff --git a/testdata/split-expected/Data/Logic/Classes/Arity.hs b/testdata/split-expected/Data/Logic/Classes/Arity.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Arity.hs
@@ -0,0 +1,11 @@
+module Data.Logic.Classes.Arity
+    ( Arity(arity)
+    ) where
+
+-- |A class that characterizes how many arguments a predicate or
+-- function takes.  Depending on the context, a result of Nothing may
+-- mean that the arity is undetermined or unknown.  However, even if
+-- this returns Nothing, the same number of arguments must be passed
+-- to all uses of a given predicate or function.
+class Arity p where
+    arity :: p -> Maybe Int
diff --git a/testdata/split-expected/Data/Logic/Classes/Atom.hs b/testdata/split-expected/Data/Logic/Classes/Atom.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Atom.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, TypeFamilies #-}
+-- | Substitution and finding variables are two basic operations on
+-- formulas that contain terms and variables.  If a formula type
+-- supports quantifiers we can also find free variables, otherwise all
+-- variables are considered free.
+module Data.Logic.Classes.Atom
+    ( Atom(..)
+    -- , Formula(..)
+    ) where
+
+import Control.Applicative.Error (Failing)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+{-
+class Formula formula term v | formula -> term v where
+    substitute :: Map.Map v term -> formula -> formula
+    allVariables :: formula -> Set.Set v
+    freeVariables :: formula -> Set.Set v
+    unify :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    match :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> formula -> r
+    isRename :: formula -> formula -> Bool
+    getSubst :: Map.Map v term -> formula -> Map.Map v term
+-}
+
+class Atom atom term v | atom -> term v where
+    substitute :: Map.Map v term -> atom -> atom
+    allVariables :: atom -> Set.Set v
+    freeVariables :: atom -> Set.Set v
+    unify :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    match :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> atom -> r
+    isRename :: atom -> atom -> Bool
+    getSubst :: Map.Map v term -> atom -> Map.Map v term
+
diff --git a/testdata/split-expected/Data/Logic/Classes/ClauseNormalForm.hs b/testdata/split-expected/Data/Logic/Classes/ClauseNormalForm.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/ClauseNormalForm.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.ClauseNormalForm
+    ( ClauseNormalFormula(clauses, makeCNF, satisfiable)
+    ) where
+
+import Control.Monad (MonadPlus)
+import Data.Logic.Classes.Negate
+import Data.Set as S
+
+-- |A class to represent formulas in CNF, which is the conjunction of
+-- a set of disjuncted literals each which may or may not be negated.
+class (Negatable lit, Eq lit, Ord lit) => ClauseNormalFormula cnf lit | cnf -> lit where
+    clauses :: cnf -> S.Set (S.Set lit)
+    makeCNF :: S.Set (S.Set lit) -> cnf
+    satisfiable :: MonadPlus m => cnf -> m Bool
diff --git a/testdata/split-expected/Data/Logic/Classes/Combine.hs b/testdata/split-expected/Data/Logic/Classes/Combine.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Combine.hs
@@ -0,0 +1,123 @@
+-- | Class Logic defines the basic boolean logic operations,
+-- AND, OR, NOT, and so on.  Definitions which pertain to both
+-- propositional and first order logic are here.
+{-# LANGUAGE DeriveDataTypeable #-}
+module Data.Logic.Classes.Combine
+    ( Combinable(..)
+    , Combination(..)
+    , combine
+    , BinOp(..)
+    , binop
+    -- * Unicode aliases for Combinable class methods
+    , (∧)
+    , (∨)
+    , (⇒)
+    , (⇔)
+    -- * Use in Harrison's code
+    , (==>)
+    , (<=>)
+    , prettyBinOp
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Negate (Negatable, (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- | A type class for logical formulas.  Minimal implementation:
+-- @
+--  (.|.)
+-- @
+class (Negatable formula) => Combinable formula where
+    -- | Disjunction/OR
+    (.|.) :: formula -> formula -> formula
+
+    -- | Derived formula combinators.  These could (and should!) be
+    -- overridden with expressions native to the instance.
+    --
+    -- | Conjunction/AND
+    (.&.) :: formula -> formula -> formula
+    x .&. y = (.~.) ((.~.) x .|. (.~.) y)
+    -- | Formula combinators: Equivalence
+    (.<=>.) :: formula -> formula -> formula
+    x .<=>. y = (x .=>. y) .&. (y .=>. x)
+    -- | Implication
+    (.=>.) :: formula -> formula -> formula
+    x .=>. y = ((.~.) x .|. y)
+    -- | Reverse implication:
+    (.<=.) :: formula -> formula -> formula
+    x .<=. y = y .=>. x
+    -- | Exclusive or
+    (.<~>.) :: formula -> formula -> formula
+    x .<~>. y = ((.~.) x .&. y) .|. (x .&. (.~.) y)
+    -- | Nor
+    (.~|.) :: formula -> formula -> formula
+    x .~|. y = (.~.) (x .|. y)
+    -- | Nand
+    (.~&.) :: formula -> formula -> formula
+    x .~&. y = (.~.) (x .&. y)
+
+infixl 1  .<=>. ,  .<~>., ⇔, <=>
+infixr 2  .=>., ⇒, ==>
+infixr 3  .|., ∨
+infixl 4  .&., ∧
+
+-- |'Combination' is a helper type used in the signatures of the
+-- 'foldPropositional' and 'foldFirstOrder' methods so can represent
+-- all the ways that formulas can be combined using boolean logic -
+-- negation, logical And, and so forth.
+data Combination formula
+    = BinOp formula BinOp formula
+    | (:~:) formula
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | A helper function for building folds:
+-- @
+--   foldPropositional combine atomic
+-- @
+-- is a no-op.
+combine :: Combinable formula => Combination formula -> formula
+combine (BinOp f1 (:<=>:) f2) = f1 .<=>. f2
+combine (BinOp f1 (:=>:) f2) = f1 .=>. f2
+combine (BinOp f1 (:&:) f2) = f1 .&. f2
+combine (BinOp f1 (:|:) f2) = f1 .|. f2
+combine ((:~:) f) = (.~.) f
+
+-- | Represents the boolean logic binary operations, used in the
+-- Combination type above.
+data BinOp
+    = (:<=>:)  -- ^ Equivalence
+    |  (:=>:)  -- ^ Implication
+    |  (:&:)  -- ^ AND
+    |  (:|:)  -- ^ OR
+    deriving (Eq, Ord, Data, Typeable, Enum, Bounded, Show, Read)
+
+binop :: Combinable formula => formula -> BinOp -> formula -> formula
+binop a (:&:) b = a .&. b
+binop a (:|:) b = a .|. b
+binop a (:=>:) b = a .=>. b
+binop a (:<=>:) b = a .<=>. b
+
+(∧) :: Combinable formula => formula -> formula -> formula
+(∧) = (.&.)
+(∨) :: Combinable formula => formula -> formula -> formula
+(∨) = (.|.)
+-- | ⇒ can't be a function when -XUnicodeSyntax is enabled.
+(⇒) :: Combinable formula => formula -> formula -> formula
+(⇒) = (.=>.)
+(⇔) :: Combinable formula => formula -> formula -> formula
+(⇔) = (.<=>.)
+
+(==>) :: Combinable formula => formula -> formula -> formula
+(==>) = (.=>.)
+(<=>) :: Combinable formula => formula -> formula -> formula
+(<=>) = (.<=>.)
+
+prettyBinOp :: BinOp -> Doc
+prettyBinOp (:<=>:) = text "⇔"
+prettyBinOp (:=>:) = text "⇒"
+prettyBinOp (:&:) = text "∧"
+prettyBinOp (:|:) = text "∨"
+
+instance Pretty BinOp where
+    pretty = prettyBinOp
diff --git a/testdata/split-expected/Data/Logic/Classes/Constants.hs b/testdata/split-expected/Data/Logic/Classes/Constants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Constants.hs
@@ -0,0 +1,40 @@
+module Data.Logic.Classes.Constants
+    ( Constants(asBool, fromBool)
+    , ifElse
+    , true
+    , (⊨)
+    , false
+    , (⊭)
+    , prettyBool
+    ) where
+
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- |Some types in the Logic class heirarchy need to have True and
+-- False elements.
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+true :: Constants p => p
+true = fromBool True
+
+false :: Constants p => p
+false = fromBool False
+
+ifElse :: a -> a -> Bool -> a
+ifElse t _ True = t
+ifElse _ f False = f
+
+(⊨) :: Constants formula => formula
+(⊨) = true
+(⊭) :: Constants formula => formula
+(⊭) = false
+
+prettyBool :: Bool -> Doc
+prettyBool True = text "⊨"
+prettyBool False = text "⊭"
+
+instance Pretty Bool where
+    pretty = prettyBool
diff --git a/testdata/split-expected/Data/Logic/Classes/Equals.hs b/testdata/split-expected/Data/Logic/Classes/Equals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Equals.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+-- | Support for equality.
+module Data.Logic.Classes.Equals
+    ( AtomEq(..)
+    , applyEq
+    , PredicateName(..)
+    , zipAtomsEq
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , pApp, pApp0, pApp1, pApp2, pApp3, pApp4, pApp5, pApp6, pApp7
+    , showFirstOrderFormulaEq
+    , (.=.), (≡)
+    , (.!=.), (≢)
+    , fromAtomEq
+    , showAtomEq
+    , prettyAtomEq
+    , varAtomEq
+    , substAtomEq
+    , funcsAtomEq
+    ) where
+
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Data.Logic.Classes.Term (Term, convertTerm, showTerm, prettyTerm, fvt, tsubst, funcs)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), (<+>), text, empty, parens, hcat, nest)
+
+-- | Its not safe to make Atom a superclass of AtomEq, because the Atom methods will fail on AtomEq instances.
+class Predicate p => AtomEq atom p term | atom -> p term, term -> atom p where
+    foldAtomEq :: (p -> [term] -> r) -> (Bool -> r) -> (term -> term -> r) -> atom -> r
+    equals :: term -> term -> atom
+    applyEq' :: p -> [term] -> atom
+
+-- | applyEq' with an arity check - clients should always call this.
+applyEq :: AtomEq atom p term => p -> [term] -> atom
+applyEq p ts =
+    case arity p of
+      Just n | n /= length ts -> arityError p ts
+      _ -> applyEq' p ts
+
+-- | A way to represent any predicate's name.  Frequently the equality
+-- predicate has no standalone representation in the p type, it is
+-- just a constructor in the atom type, or even the formula type.
+data PredicateName p = Named p Int | Equals deriving (Eq, Ord, Show)
+
+instance (Pretty p, Ord p) => Pretty (PredicateName p) where
+    pretty Equals = text "="
+    pretty (Named p _) = pretty p
+
+zipAtomsEq :: AtomEq atom p term =>
+              (p -> [term] -> p -> [term] -> Maybe r)
+           -> (Bool -> Bool -> Maybe r)
+           -> (term -> term -> term -> term -> Maybe r)
+           -> atom -> atom -> Maybe r
+zipAtomsEq ap tf eq a1 a2 =
+    foldAtomEq ap' tf' eq' a1
+    where
+      ap' p1 ts1 = foldAtomEq (ap p1 ts1) (\ _ -> Nothing) (\ _ _ -> Nothing) a2
+      tf' x1 = foldAtomEq (\ _ _ -> Nothing) (tf x1) (\ _ _ -> Nothing) a2
+      eq' t1 t2 = foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) (eq t1 t2) a2
+
+apply0 :: AtomEq atom p term => p -> atom
+apply0 p = if fromMaybe 0 (arity p) == 0 then applyEq' p [] else arityError p []
+apply1 :: AtomEq atom p a => p -> a -> atom
+apply1 p a = if fromMaybe 1 (arity p) == 1 then applyEq' p [a] else arityError p [a]
+apply2 :: AtomEq atom p a => p -> a -> a -> atom
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then applyEq' p [a,b] else arityError p [a,b]
+apply3 :: AtomEq atom p a => p -> a -> a -> a -> atom
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then applyEq' p [a,b,c] else arityError p [a,b,c]
+apply4 :: AtomEq atom p a => p -> a -> a -> a -> a -> atom
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then applyEq' p [a,b,c,d] else arityError p [a,b,c,d]
+apply5 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> atom
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then applyEq' p [a,b,c,d,e] else arityError p [a,b,c,d,e]
+apply6 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> atom
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then applyEq' p [a,b,c,d,e,f] else arityError p [a,b,c,d,e,f]
+apply7 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> a -> atom
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then applyEq' p [a,b,c,d,e,f,g] else arityError p [a,b,c,d,e,f,g]
+
+arityError :: (Arity p) => p -> [a] -> t
+arityError _p _ts = error "arity error"
+-- arityError :: (Arity p, Pretty p) => p -> [a] -> t
+-- arityError p ts = error $ "arity error: " ++ show (length ts) ++ " arguments applied to arity " ++ show (arity p) ++ " predicate " ++ show (pretty p)
+
+pApp :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> [term] -> formula
+pApp p ts = atomic (applyEq p ts)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a)
+pApp2 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b)
+pApp3 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c)
+pApp4 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d)
+pApp5 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e)
+pApp6 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f)
+pApp7 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g)
+
+showFirstOrderFormulaEq :: (FirstOrderFormula fof atom v, AtomEq atom p term, Show term, Show v, Show p) => fof -> String
+showFirstOrderFormulaEq fm =
+    fst (sfo fm)
+    where
+      sfo p = foldFirstOrder qu co tf pr p
+      qu op v f = (showQuant op ++ " " ++ show v ++ " " ++ parens quantPrec (sfo f), quantPrec)
+      co ((:~:) p) =
+          let prec' = 5 in
+          ("(.~.)" ++ parens prec' (sfo p), prec')
+      co (BinOp p op q) = (parens (opPrec op) (sfo p) ++ " " ++ showBinOp op ++ " " ++ parens (opPrec op) (sfo q), opPrec op)
+      tf x = (if x then "true" else "false", 0)
+      pr = foldAtomEq (\ p ts -> ("pApp " ++ show p ++ " " ++ show ts, 6))
+                      (\ x -> (if x then "true" else "false", 0))
+                      (\ t1 t2 -> ("(" ++ show t1 ++ ") .=. (" ++ show t2 ++ ")", 6))
+      showBinOp (:<=>:) = ".<=>."
+      showBinOp (:=>:) = ".=>."
+      showBinOp (:&:) = ".&."
+      showBinOp (:|:) = ".|."
+      showQuant Exists = "exists"
+      showQuant Forall = "for_all"
+      opPrec (:|:) = 3
+      opPrec (:&:) = 4
+      opPrec (:=>:) = 2
+      opPrec (:<=>:) = 2
+      quantPrec = 1
+      parens :: Int -> (String, Int) -> String
+      parens prec' (s, prec) = if prec >= prec' then "(" ++ s ++ ")" else s
+
+infix 5 .=., .!=., ≡, ≢
+
+(.=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .=. b = atomic (equals a b)
+
+(.!=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .!=. b = (.~.) (a .=. b)
+
+(≡) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≡) = (.=.)
+
+(≢) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≢) = (.!=.)
+
+{-
+instance (AtomEq atom p term, Constants atom, Variable v, Term term v f) => Formula atom term v where
+    substitute env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+    allVariables = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+    freeVariables = allVariables
+-}
+
+fromAtomEq :: (AtomEq atom1 p1 term1, Term term1 v1 f1,
+               AtomEq atom2 p2 term2, Term term2 v2 f2, Constants atom2) =>
+              (v1 -> v2) -> (p1 -> p2) -> (f1 -> f2) -> atom1 -> atom2
+fromAtomEq cv cp cf atom =
+    foldAtomEq (\ pr ts -> applyEq (cp pr) (map ct ts))
+               fromBool
+               (\ a b -> ct a `equals` ct b)
+               atom
+    where
+      ct = convertTerm cv cf
+
+showAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showAtomEq =
+    foldAtomEq (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+               (\ x -> if x then "true" else "false")
+               (\ t1 t2 -> "(" ++ parenTerm t1 ++ " .=. " ++ parenTerm t2 ++ ")")
+    where
+      parenTerm :: term -> String
+      parenTerm x = "(" ++ showTerm x ++ ")"
+
+prettyAtomEq :: (AtomEq atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyAtomEq pv pp pf prec atom =
+    foldAtomEq (\ p ts -> pp p <> case ts of
+                                    [] -> empty
+                                    _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+               (text . ifElse "true" "false")
+               (\ t1 t2 -> parensIf (prec > 6) (prettyTerm pv pf t1 <+> text "=" <+> prettyTerm pv pf t2))
+               atom
+    where
+      parensIf False = id
+      parensIf _ = parens . nest 1
+
+-- | Return the variables that occur in an instance of AtomEq.
+varAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f) => atom -> Set.Set v
+varAtomEq = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+
+substAtomEq :: (AtomEq atom p term, Constants atom, Term term v f) =>
+               Map.Map v term -> atom -> atom
+substAtomEq env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+
+funcsAtomEq :: (AtomEq atom p term, Term term v f, Ord f) => atom -> Set.Set (f, Int)
+funcsAtomEq = foldAtomEq (\ _ ts -> Set.unions (map funcs ts)) (const Set.empty) (\ t1 t2 -> Set.union (funcs t1) (funcs t2))
diff --git a/testdata/split-expected/Data/Logic/Classes/FirstOrder.hs b/testdata/split-expected/Data/Logic/Classes/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/FirstOrder.hs
@@ -0,0 +1,303 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+module Data.Logic.Classes.FirstOrder
+    ( FirstOrderFormula(..)
+    , Quant(..)
+    , zipFirstOrder
+    , pApp
+    , pApp0
+    , pApp1
+    , pApp2
+    , pApp3
+    , pApp4
+    , pApp5
+    , pApp6
+    , pApp7
+    , for_all'
+    , exists'
+    , quant
+    , (!)
+    , (?)
+    , (∀)
+    , (∃)
+    , quant'
+    , convertFOF
+    , toPropositional
+    , withUnivQuants
+    , showFirstOrder
+    , prettyFirstOrder
+    , fixityFirstOrder
+    , foldAtomsFirstOrder
+    , mapAtomsFirstOrder
+    , onatoms
+    , overatoms
+    , atom_union
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), apply, apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7)
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import qualified Data.Logic.Classes.Propositional as P
+import Data.Logic.Classes.Variable (Variable)
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), (<+>), text, parens, nest)
+
+-- |The 'FirstOrderFormula' type class.  Minimal implementation:
+-- @for_all, exists, foldFirstOrder, foldTerm, (.=.), pApp0-pApp7, fApp, var@.  The
+-- functional dependencies are necessary here so we can write
+-- functions that don't fix all of the type parameters.  For example,
+-- without them the univquant_free_vars function gives the error @No
+-- instance for (FirstOrderFormula Formula atom V)@ because the
+-- function doesn't mention the Term type.
+class ( Formula formula atom
+      , Combinable formula  -- Basic logic operations
+      , Constants formula
+      , Constants atom
+      , HasFixity atom
+      , Variable v
+      , Pretty atom, Pretty v
+      ) => FirstOrderFormula formula atom v | formula -> atom v where
+    -- | Universal quantification - for all x (formula x)
+    for_all :: v -> formula -> formula
+    -- | Existential quantification - there exists x such that (formula x)
+    exists ::  v -> formula -> formula
+
+    -- | A fold function similar to the one in 'PropositionalFormula'
+    -- but extended to cover both the existing formula types and the
+    -- ones introduced here.  @foldFirstOrder (.~.) quant binOp infixPred pApp@
+    -- is a no op.  The argument order is taken from Logic-TPTP.
+    foldFirstOrder :: (Quant -> v -> formula -> r)
+                   -> (Combination formula -> r)
+                   -> (Bool -> r)
+                   -> (atom -> r)
+                   -> formula
+                   -> r
+
+zipFirstOrder :: FirstOrderFormula formula atom v =>
+                 (Quant -> v -> formula -> Quant -> v -> formula -> Maybe r)
+              -> (Combination formula -> Combination formula -> Maybe r)
+              -> (Bool -> Bool -> Maybe r)
+              -> (atom -> atom -> Maybe r)
+              -> formula -> formula -> Maybe r
+zipFirstOrder qu co tf at fm1 fm2 =
+    foldFirstOrder qu' co' tf' at' fm1
+    where
+      qu' op1 v1 p1 = foldFirstOrder (qu op1 v1 p1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      co' c1 = foldFirstOrder (\ _ _ _ -> Nothing) (co c1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' atom1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at atom1) fm2
+
+-- |The 'Quant' and 'InfixPred' types, like the BinOp type in
+-- 'Data.Logic.Propositional', could be additional parameters to the type
+-- class, but it would add additional complexity with unclear
+-- benefits.
+data Quant = Forall | Exists deriving (Eq,Ord,Show,Read,Data,Typeable,Enum,Bounded)
+
+pApp :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> [term] -> formula
+pApp p ts = atomic (apply p ts :: atom)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a :: atom)
+pApp2 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b :: atom)
+pApp3 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c :: atom)
+pApp4 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d :: atom)
+pApp5 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e :: atom)
+pApp6 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f :: atom)
+pApp7 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g :: atom)
+
+-- |for_all with a list of variables, for backwards compatibility.
+for_all' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+for_all' vs f = foldr for_all f vs
+
+-- |exists with a list of variables, for backwards compatibility.
+exists' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+exists' vs f = foldr for_all f vs
+
+-- |Names for for_all and exists inspired by the conventions of the
+-- TPTP project.
+(!) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(!) = for_all
+(?) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(?) = exists
+
+-- Irrelevant, because these are always used as prefix operators, never as infix.
+infixr 9 !, ?, ∀, ∃
+
+-- | ∀ can't be a function when -XUnicodeSyntax is enabled.
+(∀) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∀) = for_all
+(∃) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∃) = exists
+
+-- | Helper function for building folds.
+quant :: FirstOrderFormula formula atom v => 
+         Quant -> v -> formula -> formula
+quant Forall v f = for_all v f
+quant Exists v f = exists v f
+
+-- |Legacy version of quant from when we supported lists of quantified
+-- variables.  It also has the virtue of eliding quantifications with
+-- empty variable lists (by calling for_all' and exists'.)
+quant' :: FirstOrderFormula formula atom v => 
+         Quant -> [v] -> formula -> formula
+quant' Forall = for_all'
+quant' Exists = exists'
+
+convertFOF :: (FirstOrderFormula formula1 atom1 v1, FirstOrderFormula formula2 atom2 v2) =>
+              (atom1 -> atom2) -> (v1 -> v2) -> formula1 -> formula2
+convertFOF convertA convertV formula =
+    foldFirstOrder qu co tf (atomic . convertA) formula
+    where
+      convert' = convertFOF convertA convertV
+      qu x v f = quant x (convertV v) (convert' f)
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+
+-- |Try to convert a first order logic formula to propositional.  This
+-- will return Nothing if there are any quantifiers, or if it runs
+-- into an atom that it is unable to convert.
+toPropositional :: forall formula1 atom v formula2 atom2.
+                   (FirstOrderFormula formula1 atom v,
+                    P.PropositionalFormula formula2 atom2) =>
+                   (atom -> atom2) -> formula1 -> formula2
+toPropositional convertAtom formula =
+    foldFirstOrder qu co tf at formula
+    where
+      convert' = toPropositional convertAtom
+      qu _ _ _ = error "toPropositional: invalid argument"
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+      at = atomic . convertAtom
+
+-- | Display a formula in a format that can be read into the interpreter.
+showFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v, Show v) => (atom -> String) -> formula -> String
+showFirstOrder sa formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu Forall v f = "(for_all " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
+      qu Exists v f = "(exists " ++  show v ++ " " ++ showFirstOrder sa f ++ ")"
+      co (BinOp f1 op f2) = "(" ++ parenForm f1 ++ " " ++ showCombine op ++ " " ++ parenForm f2 ++ ")"
+      co ((:~:) f) = "((.~.) " ++ showFirstOrder sa f ++ ")"
+      tf x = if x then "true" else "false"
+      at :: atom -> String
+      at = sa
+      parenForm x = "(" ++ showFirstOrder sa x ++ ")"
+      showCombine (:<=>:) = ".<=>."
+      showCombine (:=>:) = ".=>."
+      showCombine (:&:) = ".&."
+      showCombine (:|:) = ".|."
+
+prettyFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v) =>
+                      (Int -> atom -> Doc) -> (v -> Doc) -> Int -> formula -> Doc
+prettyFirstOrder pa pv pprec formula =
+    parensIf (pprec > prec) $
+    foldFirstOrder
+          (\ qop v f -> prettyQuant qop <> pv v <> text "." <+> (prettyFirstOrder pa pv prec f))
+          (\ cm ->
+               case cm of
+                 (BinOp f1 op f2) ->
+                     case op of
+                       (:=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:<=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:&:) -> (prettyFirstOrder pa pv 3 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:|:) -> (prettyFirstOrder pa pv 4 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                 ((:~:) f) -> text "¬" {-"~"-} <> prettyFirstOrder pa pv prec f)
+          (text . ifElse "true" "false")
+          (pa prec)
+          formula
+    where
+      Fixity prec _ = fixityFirstOrder formula
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      prettyQuant Forall = text "∀" -- "!"
+      prettyQuant Exists = text "∃" -- "?"
+
+fixityFirstOrder :: (HasFixity atom, FirstOrderFormula formula atom v) => formula -> Fixity
+fixityFirstOrder formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu _ _ _ = Fixity 10 InfixN
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- | Examine the formula to find the list of outermost universally
+-- quantified variables, and call a function with that list and the
+-- formula after the quantifiers are removed.
+withUnivQuants :: FirstOrderFormula formula atom v => ([v] -> formula -> r) -> formula -> r
+withUnivQuants fn formula =
+    doFormula [] formula
+    where
+      doFormula vs f =
+          foldFirstOrder
+                (doQuant vs)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                f
+      doQuant vs Forall v f = doFormula (v : vs) f
+      doQuant vs Exists v f = fn (reverse vs) (exists v f)
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+mapAtomsFirstOrder :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+mapAtomsFirstOrder f fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op v p = quant op v (mapAtomsFirstOrder f p)
+      co ((:~:) p) = mapAtomsFirstOrder f p
+      co (BinOp p op q) = binop (mapAtomsFirstOrder f p) op (mapAtomsFirstOrder f q)
+      tf flag = fromBool flag
+      at x = f x
+
+-- | Deprecated - use mapAtoms
+onatoms :: forall formula atom v. (FirstOrderFormula formula atom v) => (atom -> formula) -> formula -> formula
+onatoms = mapAtomsFirstOrder
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- -------------------------------------------------------------------------
+
+foldAtomsFirstOrder :: FirstOrderFormula fof atom v => (r -> atom -> r) -> r -> fof -> r
+foldAtomsFirstOrder f i fof =
+        foldFirstOrder qu co (const i) (f i) fof
+        where
+          qu _ _ fof' = foldAtomsFirstOrder f i fof'
+          co ((:~:) fof') = foldAtomsFirstOrder f i fof'
+          co (BinOp p _ q) = foldAtomsFirstOrder f (foldAtomsFirstOrder f i q) p
+
+-- | Deprecated - use foldAtoms
+overatoms :: forall formula atom v r. FirstOrderFormula formula atom v =>
+             (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsFirstOrder (flip f) b fm
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: forall formula atom v a. (FirstOrderFormula formula atom v, Ord a) =>
+              (atom -> Set.Set a) -> formula -> Set.Set a
+atom_union f fm = overatoms (\ h t -> Set.union (f h) t) fm Set.empty
+
+$(deriveSafeCopy 1 'base ''Quant)
diff --git a/testdata/split-expected/Data/Logic/Classes/Formula.hs b/testdata/split-expected/Data/Logic/Classes/Formula.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Formula.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Data.Logic.Classes.Formula
+    ( Formula(atomic, foldAtoms, mapAtoms)
+    ) where
+
+class Formula formula atom where
+    atomic :: atom -> formula
+    foldAtoms :: Formula formula atom => (r -> atom -> r) -> r -> formula -> r
+    mapAtoms :: Formula formula atom => (atom -> formula) -> formula -> formula
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs b/testdata/split-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.FoldAtomsLiteral
+    ( foldAtomsLiteral
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+
+foldAtomsLiteral :: Literal lit atom => (r -> atom -> r) -> r -> lit -> r
+foldAtomsLiteral f i lit = foldLiteral (foldAtomsLiteral f i) (const i) (f i) lit
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/FromFirstOrder.hs b/testdata/split-expected/Data/Logic/Classes/Literal/FromFirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/FromFirstOrder.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.FromFirstOrder
+    ( fromFirstOrder
+    ) where
+
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import qualified Data.Logic.Classes.FirstOrder as FOF (FirstOrderFormula(foldFirstOrder))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Failing (Failing(..))
+
+{- This makes bad things happen.
+-- | We can use an fof type as a lit, but it must not use some constructs.
+instance FirstOrderFormula fof atom v => Literal fof atom v where
+    foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm
+        where qu = error "instance Literal FirstOrderFormula"
+              co ((:~:) x) = neg x
+              co _ = error "instance Literal FirstOrderFormula"
+    atomic = Data.Logic.Classes.FirstOrder.atomic
+-}
+
+-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
+-- with a construct unsupported in a normal logic formula,
+-- i.e. quantifiers and formula combinators other than negation.
+fromFirstOrder :: forall formula atom v lit atom2.
+                  (Formula lit atom2, FOF.FirstOrderFormula formula atom v, Literal lit atom2) =>
+                  (atom -> atom2) -> formula -> Failing lit
+fromFirstOrder ca formula =
+    FOF.foldFirstOrder (\ _ _ _ -> Failure ["fromFirstOrder"]) co (Success . fromBool) (Success . atomic . ca) formula
+    where
+      co :: Combination formula -> Failing lit
+      co ((:~:) f) =  fromFirstOrder ca f >>= return . (.~.)
+      co _ = Failure ["fromFirstOrder"]
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/FromLiteral.hs b/testdata/split-expected/Data/Logic/Classes/Literal/FromLiteral.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/FromLiteral.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.FromLiteral
+    ( fromLiteral
+    ) where
+
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import qualified Data.Logic.Classes.FirstOrder as FOF (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+
+fromLiteral :: forall lit atom v fof atom2. (Literal lit atom, FOF.FirstOrderFormula fof atom2 v) =>
+               (atom -> atom2) -> lit -> fof
+fromLiteral ca lit = foldLiteral (\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs b/testdata/split-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.Internal.FixityLiteral
+    ( fixityLiteral
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..))
+
+fixityLiteral :: (Literal formula atom) => formula -> Fixity
+fixityLiteral formula =
+    foldLiteral neg tf at formula
+    where
+      neg _ = Fixity 5 InfixN
+      tf _ = Fixity 10 InfixN
+      at = fixity
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/Literal.hs b/testdata/split-expected/Data/Logic/Classes/Literal/Literal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/Literal.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.Literal
+    ( Literal(foldLiteral)
+    ) where
+
+import Data.Logic.Classes.Constants (Constants)
+import Data.Logic.Classes.Formula (Formula)
+import Data.Logic.Classes.Negate (Negatable)
+import Data.Logic.Classes.Pretty (HasFixity)
+
+-- |Literals are the building blocks of the clause and implicative normal
+-- |forms.  They support negation and must include True and False elements.
+class (Negatable lit, Constants lit, HasFixity atom, Formula lit atom, Ord lit) => Literal lit atom | lit -> atom where
+    foldLiteral :: (lit -> r) -> (Bool -> r) -> (atom -> r) -> lit -> r
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/PrettyLit.hs b/testdata/split-expected/Data/Logic/Classes/Literal/PrettyLit.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/PrettyLit.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.PrettyLit
+    ( prettyLit
+    ) where
+
+import Data.Logic.Classes.Literal.Internal.FixityLiteral (fixityLiteral)
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate (negated)
+import Data.Logic.Classes.Pretty (Fixity(Fixity))
+import Text.PrettyPrint ((<>), Doc, nest, parens, text)
+
+{-
+prettyLit :: forall lit atom term v p f. (Literal lit atom v, Apply atom p term, Term term v f) =>
+              (v -> Doc)
+           -> (p -> Doc)
+           -> (f -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pv pp pf _prec lit =
+    foldLiteral neg tf at lit
+    where
+      neg :: lit -> Doc
+      neg x = if negated x then text {-"¬"-} "~" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x
+      tf = text . ifElse "true" "false"
+      at = foldApply (\ pr ts -> 
+                        pp pr <> case ts of
+                                   [] -> empty
+                                   _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+                   (\ x -> text $ if x then "true" else "false")
+      -- parensIf False = id
+      -- parensIf _ = parens . nest 1
+-}
+
+prettyLit :: forall lit atom v. (Literal lit atom) =>
+              (Int -> atom -> Doc)
+           -> (v -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pa pv pprec lit =
+    parensIf (pprec > prec) $ foldLiteral co tf at lit
+    where
+      co :: lit -> Doc
+      co x = if negated x then text {-"¬"-} "~" <> prettyLit pa pv 5 x else prettyLit pa pv 5 x
+      tf x = text (if x then "true" else "false")
+      at = pa 6
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      Fixity prec _ = fixityLiteral lit
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/ToPropositional.hs b/testdata/split-expected/Data/Logic/Classes/Literal/ToPropositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/ToPropositional.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.ToPropositional
+    ( toPropositional
+    ) where
+
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
+
+toPropositional :: forall lit atom pf atom2. (Literal lit atom, P.PropositionalFormula pf atom2) =>
+                   (atom -> atom2) -> lit -> pf
+toPropositional ca lit = foldLiteral (\ p -> (.~.) (toPropositional ca p)) fromBool (atomic . ca) lit
diff --git a/testdata/split-expected/Data/Logic/Classes/Literal/ZipLiterals.hs b/testdata/split-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.ZipLiterals
+    ( zipLiterals
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+
+zipLiterals :: Literal lit atom =>
+               (lit -> lit -> Maybe r)
+            -> (Bool -> Bool -> Maybe r)
+            -> (atom -> atom -> Maybe r)
+            -> lit -> lit -> Maybe r
+zipLiterals neg tf at fm1 fm2 =
+    foldLiteral neg' tf' at' fm1
+    where
+      neg' p1 = foldLiteral (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldLiteral (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' a1 = foldLiteral (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
diff --git a/testdata/split-expected/Data/Logic/Classes/Negate.hs b/testdata/split-expected/Data/Logic/Classes/Negate.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Negate.hs
@@ -0,0 +1,42 @@
+module Data.Logic.Classes.Negate
+     ( Negatable(..)
+     , negated
+     , (.~.)
+     , (¬)
+     , negative
+     , positive
+     ) where
+
+-- |The class of formulas that can be negated.  There are some types
+-- that can be negated but do not support the other Boolean Logic
+-- operators, such as the 'Literal' class.
+class Negatable formula where
+    -- | Negate a formula in a naive fashion, the operators below
+    -- prevent double negation.
+    negatePrivate :: formula -> formula
+    -- | Test whether a formula is negated or normal
+    foldNegation :: (formula -> r) -- ^ called for normal formulas
+                 -> (formula -> r) -- ^ called for negated formulas
+                 -> formula -> r
+-- | Is this formula negated at the top level?
+negated :: Negatable formula => formula -> Bool
+negated = foldNegation (const False) (not . negated)
+
+-- | Negate the formula, avoiding double negation
+(.~.) :: Negatable formula => formula -> formula
+(.~.) = foldNegation negatePrivate id
+
+(¬) :: Negatable formula => formula -> formula
+(¬) = (.~.)
+
+infix 5 .~., ¬
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.  (These names are used in Harrison's code.)
+-- ------------------------------------------------------------------------- 
+
+negative :: Negatable formula => formula -> Bool
+negative = negated
+
+positive :: Negatable formula => formula -> Bool
+positive = not . negative
diff --git a/testdata/split-expected/Data/Logic/Classes/Pretty.hs b/testdata/split-expected/Data/Logic/Classes/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Pretty.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Classes.Pretty
+    ( Pretty(pretty)
+    , HasFixity(fixity)
+    , TH.Fixity(..)
+    , TH.FixityDirection(..)
+    , topFixity
+    , botFixity
+    ) where
+
+import qualified Language.Haskell.TH.Syntax as TH
+import Text.PrettyPrint (Doc, text)
+
+-- | The intent of this class is to be similar to Show, but only one
+-- way, with no corresponding Read class.  It doesn't really belong
+-- here in logic-classes.  To put something in a pretty printing class
+-- implies that there is only one way to pretty print it, which is not
+-- an assumption made by Text.PrettyPrint.  But in practice this is
+-- often good enough.
+class Pretty x where
+    pretty :: x -> Doc
+
+-- | A class used to do proper parenthesization of formulas.  If we
+-- nest a higher precedence formula inside a lower one parentheses can
+-- be omitted.  Because @|@ has lower precedence than @&@, the formula
+-- @a | (b & c)@ appears as @a | b & c@, while @(a | b) & c@ appears
+-- unchanged.  (Name Precedence chosen because Fixity was taken.)
+-- 
+-- The second field of Fixity is the FixityDirection, which can be
+-- left, right, or non.  A left associative operator like @/@ is
+-- grouped left to right, so parenthese can be omitted from @(a / b) /
+-- c@ but not from @a / (b / c)@.  It is a syntax error to omit
+-- parentheses when formatting a non-associative operator.
+-- 
+-- The Haskell FixityDirection type is concerned with how to interpret
+-- a formula formatted in a certain way, but here we are concerned
+-- with how to format a formula given its interpretation.  As such,
+-- one case the Haskell type does not capture is whether the operator
+-- follows the associative law, so we can omit parentheses in an
+-- expression such as @a & b & c@.
+class HasFixity x where
+    fixity :: x -> TH.Fixity
+
+-- Definitions from template-haskell:
+-- data Fixity = Fixity Int FixityDirection
+-- data FixityDirection = InfixL | InfixR | InfixN
+
+-- | This is used as the initial value for the parent fixity.
+topFixity :: TH.Fixity
+topFixity = TH.Fixity 0 TH.InfixN
+
+-- | This is used as the fixity for things that never need
+-- parenthesization, such as function application.
+botFixity :: TH.Fixity
+botFixity = TH.Fixity 10 TH.InfixN
+
+instance Pretty String where
+    pretty = text
diff --git a/testdata/split-expected/Data/Logic/Classes/Propositional.hs b/testdata/split-expected/Data/Logic/Classes/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Propositional.hs
@@ -0,0 +1,333 @@
+-- | PropositionalFormula is a multi-parameter type class for
+-- representing instance of propositional (aka zeroth order) logic
+-- datatypes.  These are formulas which have truth values, but no "for
+-- all" or "there exists" quantifiers and thus no variables or terms
+-- as we have in first order or predicate logic.  It is intended that
+-- we will be able to write instances for various different
+-- implementations to allow these systems to interoperate.  The
+-- operator names were adopted from the Logic-TPTP package.
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Classes.Propositional
+    ( PropositionalFormula(..)
+    , showPropositional
+    , prettyPropositional
+    , fixityPropositional
+    , convertProp
+    , combine
+    , negationNormalForm
+    , clauseNormalForm
+    , clauseNormalForm'
+    , clauseNormalFormAlt
+    , clauseNormalFormAlt'
+    , disjunctiveNormalForm
+    , disjunctiveNormalForm'
+    , overatoms
+    , foldAtomsPropositional
+    , mapAtomsPropositional
+    ) where
+
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool, prettyBool)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate
+import Data.Logic.Classes.Pretty (Pretty, HasFixity(fixity), Fixity(Fixity), FixityDirection(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as Set
+import Text.PrettyPrint (Doc, text, (<>))
+
+-- |A type class for propositional logic.  If the type we are writing
+-- an instance for is a zero-order (aka propositional) logic type
+-- there will generally by a type or a type parameter corresponding to
+-- atom.  For first order or predicate logic types, it is generally
+-- easiest to just use the formula type itself as the atom type, and
+-- raise errors in the implementation if a non-atomic formula somehow
+-- appears where an atomic formula is expected (i.e. as an argument to
+-- atomic or to the third argument of foldPropositional.)
+-- 
+-- The Ord superclass is required so we can put formulas in sets
+-- during the normal form computations.  Negatable and Combinable are
+-- also considered basic operations that we can't build this package
+-- without.  It is less obvious whether Constants is always required,
+-- but the implementation of functions like simplify would be more
+-- elaborate if we didn't have it, so we will require it.
+class (Ord formula, Negatable formula, Combinable formula, Constants formula,
+       Pretty formula, HasFixity formula, Formula formula atom) => PropositionalFormula formula atom | formula -> atom where
+    -- | Build an atomic formula from the atom type.
+    -- | A fold function that distributes different sorts of formula
+    -- to its parameter functions, one to handle binary operators, one
+    -- for negations, and one for atomic formulas.  See examples of its
+    -- use to implement the polymorphic functions below.
+    foldPropositional :: (Combination formula -> r)
+                      -> (Bool -> r)
+                      -> (atom -> r)
+                      -> formula -> r
+
+-- | Show a formula in a format that can be evaluated 
+showPropositional :: (PropositionalFormula formula atom) => (atom -> String) -> formula -> String
+showPropositional showAtom formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) f) = "(.~.) " ++ parenForm f
+      co (BinOp f1 op f2) = parenForm f1 ++ " " ++ showFormOp op ++ " " ++ parenForm f2
+      tf True = "true"
+      tf False = "false"
+      at = showAtom
+      parenForm x = "(" ++ showPropositional showAtom x ++ ")"
+      showFormOp (:<=>:) = ".<=>."
+      showFormOp (:=>:) = ".=>."
+      showFormOp (:&:) = ".&."
+      showFormOp (:|:) = ".|."
+
+-- | Show a formula in a visually pleasing format.
+prettyPropositional :: (PropositionalFormula formula atom, HasFixity formula) =>
+                       (atom -> Doc)
+                    -> Fixity        -- ^ The fixity of the parent formula.  If the operator being formatted here
+                                     -- has a lower precedence it needs to be parenthesized.
+                    -> formula
+                    -> Doc
+prettyPropositional prettyAtom (Fixity pprec _pdir) formula =
+    parenIf (pprec > prec) (foldPropositional co tf at formula)
+    where
+      co ((:~:) f) = text "¬" <> prettyPropositional prettyAtom fix f
+      co (BinOp f1 op f2) = prettyPropositional prettyAtom fix f1 <> text " " <> prettyBinOp op <> text " " <> prettyPropositional prettyAtom fix f2
+      tf = prettyBool
+      at = prettyAtom
+      -- parenForm x = cat [text "(", prettyPropositional prettyAtom 0 x, text ")"]
+      parenIf True x = text "(" <> x <> text ")"
+      parenIf False x = x
+      fix@(Fixity prec _dir) = fixity formula
+
+fixityPropositional :: (HasFixity atom, PropositionalFormula formula atom) => formula -> Fixity
+fixityPropositional formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- |Convert any instance of a propositional logic expression to any
+-- other using the supplied atom conversion function.
+convertProp :: forall formula1 atom1 formula2 atom2.
+               (PropositionalFormula formula1 atom1,
+                PropositionalFormula formula2 atom2) =>
+               (atom1 -> atom2) -> formula1 -> formula2
+convertProp convertA formula =
+    foldPropositional c fromBool a formula
+    where
+      convert' = convertProp convertA
+      c ((:~:) f) = (.~.) (convert' f)
+      c (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      a = atomic . convertA
+
+-- | Simplify and recursively apply nnf.
+negationNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+negationNormalForm = nnf . psimplify
+
+-- |Eliminate => and <=> and move negations inwards:
+-- 
+-- @
+-- Formula      Rewrites to
+--  P => Q      ~P | Q
+--  P <=> Q     (P & Q) | (~P & ~Q)
+-- ~∀X P        ∃X ~P
+-- ~∃X P        ∀X ~P
+-- ~(P & Q)     (~P | ~Q)
+-- ~(P | Q)     (~P & ~Q)
+-- ~~P  P
+-- @
+-- 
+nnf :: (PropositionalFormula formula atom) => formula -> formula
+nnf fm = foldPropositional (nnfCombine fm) fromBool (\ _ -> fm) fm
+
+nnfCombine :: (PropositionalFormula formula atom) => formula -> Combination formula -> formula
+nnfCombine fm ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+nnfCombine _ (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+nnfCombine _ (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+nnfCombine _ (BinOp p (:&:) q) = nnf p .&. nnf q
+nnfCombine _ (BinOp p (:|:) q) = nnf p .|. nnf q
+
+nnfNotCombine :: (PropositionalFormula formula atom) => Combination formula -> formula
+nnfNotCombine ((:~:) p) = nnf p
+nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- |Do a bottom-up recursion to simplify a propositional formula.
+psimplify :: (PropositionalFormula formula atom) => formula -> formula
+psimplify fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      co (BinOp p (:&:) q) = psimplify1 (psimplify p .&. psimplify q)
+      co (BinOp p (:|:) q) = psimplify1 (psimplify p .|. psimplify q)
+      co (BinOp p (:=>:) q) = psimplify1 (psimplify p .=>. psimplify q)
+      co (BinOp p (:<=>:) q) = psimplify1 (psimplify p .<=>. psimplify q)
+      tf _ = fm
+      at _ = fm
+
+-- |Do one step of simplify for propositional formulas:
+-- Perform the following transformations everywhere, plus any
+-- commuted versions for &, |, and <=>.
+-- 
+-- @
+--  ~False      -> True
+--  ~True       -> False
+--  True & P    -> P
+--  False & P   -> False
+--  True | P    -> True
+--  False | P   -> P
+--  True => P   -> P
+--  False => P  -> True
+--  P => True   -> P
+--  P => False  -> True
+--  True <=> P  -> P
+--  False <=> P -> ~P
+-- @
+-- 
+psimplify1 :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) f) = foldPropositional simplifyNotCombine (fromBool . not) simplifyNotAtom f
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _)            -> r
+            (Just False, (:&:), _)            -> fromBool False
+            (_,          (:&:), Just True)    -> l
+            (_,          (:&:), Just False)   -> fromBool False
+            (Just True,  (:|:), _)            -> fromBool True
+            (Just False, (:|:), _)            -> r
+            (_,          (:|:), Just True)    -> fromBool True
+            (_,          (:|:), Just False)   -> l
+            (Just True,  (:=>:), _)           -> r
+            (Just False, (:=>:), _)           -> fromBool True
+            (_,          (:=>:), Just True)   -> fromBool True
+            (_,          (:=>:), Just False)  -> (.~.) l
+            (Just False, (:<=>:), Just False) -> fromBool True
+            (Just True,  (:<=>:), _)          -> r
+            (Just False, (:<=>:), _)          -> (.~.) r
+            (_,          (:<=>:), Just True)  -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _                                 -> fm
+      simplifyNotCombine ((:~:) f) = f
+      simplifyNotCombine _ = fm
+      simplifyNotAtom x = (.~.) (atomic x)
+
+clauseNormalForm' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalForm' = simp purecnf . negationNormalForm
+
+clauseNormalForm :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalForm formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalForm' formula
+
+-- |I'm not sure of the clauseNormalForm functions above are wrong or just different.
+clauseNormalFormAlt' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalFormAlt' = simp purecnf' . negationNormalForm
+
+clauseNormalFormAlt :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalFormAlt formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalFormAlt' formula
+
+disjunctiveNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+disjunctiveNormalForm formula =
+    case clean (lists dnf) of
+      [] -> fromBool False
+      xss -> foldr1 (.|.) . map (foldr1 (.&.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      dnf = disjunctiveNormalForm' formula
+
+disjunctiveNormalForm' :: (PropositionalFormula formula atom, Eq formula) => formula -> Set.Set (Set.Set formula)
+disjunctiveNormalForm' = simp purednf . negationNormalForm
+
+simp :: forall formula atom. (PropositionalFormula formula atom) =>
+        (formula -> Set.Set (Set.Set formula)) -> formula -> Set.Set (Set.Set formula)
+simp purenf fm =
+    case (compare fm (fromBool False), compare fm (fromBool True)) of
+      (EQ, _) -> Set.empty
+      (_, EQ) -> Set.singleton Set.empty
+      _ ->cjs'
+    where
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (Set.isProperSubsetOf x) cjs))
+      cjs = Set.filter (not . trivial) (purenf (nnf fm)) :: Set.Set (Set.Set formula)
+
+-- |Harrison page 59.  Look for complementary pairs in a clause.
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection (Set.map (.~.) n) p
+    where (n, p) = Set.partition negated lits
+
+purecnf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+purednf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purednf fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.distrib (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+purecnf' :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf' fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.union (purecnf' p) (purecnf' q)
+      c (BinOp p (:|:) q) = Set.distrib (purecnf' p) (purecnf' q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+-- | Use this to implement foldAtoms
+foldAtomsPropositional :: PropositionalFormula pf atom => (r -> atom -> r) -> r -> pf -> r
+foldAtomsPropositional f i pf =
+        foldPropositional co (const i) (f i) pf
+        where
+          co ((:~:) pf') = foldAtomsPropositional f i pf'
+          co (BinOp p _ q) = foldAtomsPropositional f (foldAtomsPropositional f i q) p
+
+-- | Deprecated - use foldAtoms.
+overatoms :: forall formula atom r. PropositionalFormula formula atom => (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsPropositional (flip f) b fm
+
+mapAtomsPropositional :: forall formula atom. PropositionalFormula formula atom => (atom -> formula) -> formula -> formula
+mapAtomsPropositional f fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = mapAtomsPropositional f p
+      co (BinOp p op q) = binop (mapAtomsPropositional f p) op (mapAtomsPropositional f q)
+      tf flag = fromBool flag
+      at x = f x
+
+$(deriveSafeCopy 1 'base ''BinOp)
+$(deriveSafeCopy 1 'base ''Combination)
diff --git a/testdata/split-expected/Data/Logic/Classes/Skolem.hs b/testdata/split-expected/Data/Logic/Classes/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Skolem.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.Skolem where
+
+import Data.Logic.Classes.Variable (Variable)
+
+-- |This class shows how to convert between atomic Skolem functions
+-- and Ints.  We include a variable type as a parameter because we
+-- create skolem functions to replace an existentially quantified
+-- variable, and it can be helpful to retain a reference to the
+-- variable.
+class Variable v => Skolem f v | f -> v where
+    toSkolem :: v -> f
+    -- ^ Built a Skolem function from the given variable and number.
+    -- The number is generally obtained from the skolem monad.
+    isSkolem  :: f -> Bool
diff --git a/testdata/split-expected/Data/Logic/Classes/Term.hs b/testdata/split-expected/Data/Logic/Classes/Term.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Term.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Classes.Term
+    ( Term(..)
+    , Function
+    , convertTerm
+    , showTerm
+    , prettyTerm
+    , fvt
+    , tsubst
+    , funcs
+    ) where
+
+import Data.Generics (Data)
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Skolem
+import Data.Logic.Classes.Variable
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), brackets, hcat, text)
+
+class (Eq f, Ord f, Skolem f v, Data f, Pretty f) => Function f v
+
+class ( Ord term  -- For implementing Ord in Literal
+      , Variable v
+      , Function f v ) => Term term v f | term -> v f where
+    vt :: v -> term
+    -- ^ Build a term which is a variable reference.
+    fApp :: f -> [term] -> term
+    -- ^ Build a term by applying terms to an atomic function.  @f@
+    -- (atomic function) is one of the type parameters, this package
+    -- is mostly indifferent to its internal structure.
+    foldTerm :: (v -> r) -> (f -> [term] -> r) -> term -> r
+    -- ^ A fold for the term data type, which understands terms built
+    -- from a variable and a term built from the application of a
+    -- primitive function to other terms.
+    zipTerms :: (v -> v -> Maybe r) -> (f -> [term] -> f -> [term] -> Maybe r) -> term -> term -> Maybe r
+
+convertTerm :: forall term1 v1 f1 term2 v2 f2.
+               (Term term1 v1 f1,
+                Term term2 v2 f2) =>
+               (v1 -> v2) -> (f1 -> f2) -> term1 -> term2
+convertTerm convertV convertF term =
+    foldTerm v fn term
+    where
+      convertTerm' = convertTerm convertV convertF
+      v = vt . convertV
+      fn x ts = fApp (convertF x) (map convertTerm' ts)
+
+showTerm :: forall term v f. (Term term v f, Show v, Show f) =>
+            term -> String
+showTerm term =
+    foldTerm v f term
+    where
+      v :: v -> String
+      v v' = "vt (" ++ show v' ++ ")"
+      f :: f -> [term] -> String
+      f fn ts = "fApp (" ++ show fn ++ ") [" ++ intercalate "," (map showTerm ts) ++ "]"
+
+prettyTerm :: forall v f term. (Term term v f) =>
+              (v -> Doc)
+           -> (f -> Doc)
+           -> term
+           -> Doc
+prettyTerm pv pf t = foldTerm pv (\ fn ts -> pf fn <> brackets (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts)))) t
+
+fvt :: (Term term v f, Ord v) => term -> Set.Set v
+fvt tm = foldTerm Set.singleton (\ _ args -> Set.unions (map fvt args)) tm
+
+-- ------------------------------------------------------------------------- 
+-- Substitution within terms.                                                
+-- ------------------------------------------------------------------------- 
+
+tsubst :: (Term term v f, Ord v) => Map.Map v term -> term -> term
+tsubst sfn tm = foldTerm (\ x -> fromMaybe tm (Map.lookup x sfn)) (\ fn args -> fApp fn (map (tsubst sfn) args)) tm
+
+funcs :: (Term term v f, Ord f) => term -> Set.Set (f, Int)
+funcs tm =
+    foldTerm (const Set.empty)
+             (\ f args -> foldr (\ arg r -> Set.union (funcs arg) r) (Set.singleton (f, length args)) args)
+             tm
diff --git a/testdata/split-expected/Data/Logic/Classes/Variable.hs b/testdata/split-expected/Data/Logic/Classes/Variable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Classes/Variable.hs
@@ -0,0 +1,32 @@
+module Data.Logic.Classes.Variable
+    ( Variable(..)
+    , variants
+    , showVariable
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Pretty (Pretty)
+import qualified Data.Set as Set
+import Data.String (IsString)
+import Text.PrettyPrint (Doc)
+
+class (Ord v, IsString v, Data v, Pretty v) => Variable v where
+    variant :: v -> Set.Set v -> v
+    -- ^ Return a variable based on v but different from any set
+    -- element.  The result may be v itself if v is not a member of
+    -- the set.
+    prefix :: String -> v -> v
+    -- ^ Modify a variable by adding a prefix.  This unfortunately
+    -- assumes that v is "string-like" but at least one algorithm in
+    -- Harrison currently requires this.
+    prettyVariable :: v -> Doc
+    -- ^ Pretty print a variable
+
+-- | Return an infinite list of variations on v
+variants :: Variable v => v -> [v]
+variants v0 =
+    iter' Set.empty v0
+    where iter' s v = let v' = variant v s in v' : iter' (Set.insert v s) v'
+
+showVariable :: Variable v => v -> String
+showVariable v = "fromString (" ++ show (show (prettyVariable v)) ++ ")"
diff --git a/testdata/split-expected/Data/Logic/Failing.hs b/testdata/split-expected/Data/Logic/Failing.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Failing.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE  DeriveDataTypeable, StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Failing
+    ( Failing(Success, Failure)
+    , failing
+    ) where
+
+import Control.Applicative.Error
+import Data.Generics
+
+failing :: ([String] -> b) -> (a -> b) -> Failing a -> b
+failing f _ (Failure errs) = f errs
+failing _ f (Success a)    = f a
+
+instance Monad Failing where
+  return = Success
+  m >>= f =
+      case m of
+        (Failure errs) -> (Failure errs)
+        (Success a) -> f a
+  fail errMsg = Failure [errMsg]
+
+deriving instance Typeable1 Failing
+deriving instance Data a => Data (Failing a)
+deriving instance Read a => Read (Failing a)
+deriving instance Eq a => Eq (Failing a)
+deriving instance Ord a => Ord (Failing a)
diff --git a/testdata/split-expected/Data/Logic/Harrison/DP.hs b/testdata/split-expected/Data/Logic/Harrison/DP.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/DP.hs
@@ -0,0 +1,274 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DP
+    ( tests
+    , dpll
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.DefCNF (defcnfs, NumAtom(..))
+import Data.Logic.Harrison.Lib (allpairs, defined, maximize', minimize', setmapfilter, (|->))
+import Data.Logic.Harrison.Prop (negative, positive, trivial)
+import Data.Logic.Harrison.PropExamples (Atom(..), N, prime)
+import Data.Logic.Tests.HUnit (assertEqual, convert, Test(TestCase, TestList))
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (delete, difference, empty, filter, findMin, flatten, fold, insert, intersection, map, member, minView, null, partition, Set, singleton, size, union)
+
+instance NumAtom (Atom N) where
+    ma n = P "p" n Nothing
+    ai (P _ n _) = n
+
+tests = convert (TestList [test01, test02, test03])
+
+-- ========================================================================= 
+-- The Davis-Putnam and Davis-Putnam-Loveland-Logemann procedures.           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- The DP procedure.                                                         
+-- ------------------------------------------------------------------------- 
+
+one_literal_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+one_literal_rule clauses =
+    case Set.minView (Set.filter (\ cl -> Set.size cl == 1) clauses) of
+      Nothing -> Failure ["one_literal_rule"]
+      Just (s, _) ->
+          let u = Set.findMin s in
+          let u' = (.~.) u in
+          let clauses1 = Set.filter (\ cl -> not (Set.member u cl)) clauses in
+          Success (Set.map (\ cl -> Set.delete u' cl) clauses1)
+
+affirmative_negative_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+affirmative_negative_rule clauses =
+  let (neg',pos) = Set.partition negative (Set.flatten clauses) in
+  let neg = Set.map (.~.) neg' in
+  let pos_only = Set.difference pos neg
+      neg_only = Set.difference neg pos in
+  let pure = Set.union pos_only (Set.map (.~.) neg_only) in
+  if Set.null pure
+  then Failure ["affirmative_negative_rule"]
+  else Success (Set.filter (\ cl -> Set.null (Set.intersection cl pure)) clauses)
+
+resolve_on :: forall lit atom. (Literal lit atom, Ord lit) =>
+              lit -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit)
+resolve_on p clauses =
+  let p' = (.~.) p
+      (pos,notpos) = Set.partition (Set.member p) clauses in
+  let (neg,other) = Set.partition (Set.member p') notpos in
+  let pos' = Set.map (Set.filter (\ l -> l /= p)) pos
+      neg' = Set.map (Set.filter (\ l -> l /= p')) neg in
+  let res0 = allpairs Set.union pos' neg' in
+  Set.union other (Set.filter (not . trivial) res0)
+
+resolution_blowup :: forall formula. (Negatable formula, Ord formula) =>
+                     Set.Set (Set.Set formula) -> formula -> Int
+resolution_blowup cls l =
+  let m = Set.size (Set.filter (Set.member l) cls)
+      n = Set.size (Set.filter (Set.member ((.~.) l)) cls) in
+  m * n - m - n
+
+resolution_rule :: forall lit atom. (Literal lit atom, Ord lit) =>
+                   Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+resolution_rule clauses =
+    let pvs = Set.filter positive (Set.flatten clauses) in
+    case minimize' (resolution_blowup clauses) pvs of
+      Just p -> Success (resolve_on p clauses)
+      Nothing -> Failure ["resolution_rule"]
+
+-- ------------------------------------------------------------------------- 
+-- Overall procedure.                                                        
+-- ------------------------------------------------------------------------- 
+
+dp :: forall lit atom. (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing Bool        
+dp clauses =
+  if Set.null clauses
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dp of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dp of
+                    Success x -> Success x
+                    Failure _ -> resolution_rule clauses >>= dp
+
+-- ------------------------------------------------------------------------- 
+-- Davis-Putnam satisfiability tester and tautology checker.                 
+-- ------------------------------------------------------------------------- 
+
+dpsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dpsat fm = dp (defcnfs fm :: Set.Set (Set.Set pf))
+
+dptaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dptaut fm = dpsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test01 = TestCase (assertEqual "dptaut(prime 11)" (Success True) (dptaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- The same thing but with the DPLL procedure.                               
+-- ------------------------------------------------------------------------- 
+
+posneg_count :: forall formula. (Negatable formula, Ord formula) =>
+                Set.Set (Set.Set formula) -> formula -> Int
+posneg_count cls l =                         
+  let m = Set.size(Set.filter (Set.member l) cls)                 
+      n = Set.size(Set.filter (Set.member ((.~.) l)) cls) in
+  m + n                                  
+
+dpll :: forall lit atom. (Literal lit atom, Ord lit) =>
+        Set.Set (Set.Set lit) -> Failing Bool
+dpll clauses =       
+  if clauses == Set.empty
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dpll of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dpll of
+                    Success x -> Success x
+                    Failure _ ->
+                        let pvs = Set.filter positive (Set.flatten clauses) in
+                        case maximize' (posneg_count clauses) pvs of
+                          Nothing -> Failure ["dpll"]
+                          Just p -> 
+                              case (dpll (Set.insert (Set.singleton p) clauses), dpll (Set.insert (Set.singleton ((.~.) p)) clauses)) of
+                                (Success a, Success b) -> Success (a || b)
+                                (Failure a, Failure b) -> Failure (a ++ b)
+                                (Failure a, _) -> Failure a
+                                (_, Failure b) -> Failure b
+
+dpllsat :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+           pf -> Failing Bool
+dpllsat fm = dpll(defcnfs fm :: Set.Set (Set.Set pf))
+
+dplltaut :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+            pf -> Failing Bool
+dplltaut fm = dpllsat ((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+test02 = TestCase (assertEqual "dplltaut(prime 11)" (Success True) (dplltaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- Iterative implementation with explicit trail instead of recursion.        
+-- ------------------------------------------------------------------------- 
+
+data TrailMix = Guessed | Deduced deriving (Eq, Ord)
+
+unassigned :: forall formula. (Negatable formula, Ord formula) =>
+              Set.Set (Set.Set formula) -> Set.Set (formula, TrailMix) -> Set.Set formula
+unassigned cls trail =
+    Set.difference (Set.flatten (Set.map (Set.map litabs) cls)) (Set.map (litabs . fst) trail)
+    where litabs p = if negated p then (.~.) p else p
+
+unit_subpropagate :: forall formula. (Negatable formula, Ord formula) =>
+                     (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+                  -> (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+unit_subpropagate (cls,fn,trail) =
+  let cls' = Set.map (Set.filter (not . defined fn . (.~.))) cls in
+  let uu cs =
+          case Set.minView cs of
+            Nothing -> Failure ["unit_subpropagate"]
+            Just (c, _) -> if Set.size cs == 1 && not (defined fn c)
+                           then Success cs
+                           else Failure ["unit_subpropagate"] in
+  let newunits = Set.flatten (setmapfilter uu cls') in
+  if Set.null newunits then (cls',fn,trail) else
+  let trail' = Set.fold (\ p t -> Set.insert (p,Deduced) t) trail newunits
+      fn' = Set.fold (\ u -> (u |-> ())) fn newunits in
+  unit_subpropagate (cls',fn',trail')
+
+unit_propagate :: forall t. (Negatable t, Ord t) =>
+                  (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+               -> (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+unit_propagate (cls,trail) =
+  let fn = Set.fold (\ (x,_) -> (x |-> ())) Map.empty trail in
+  let (cls',fn',trail') = unit_subpropagate (cls,fn,trail) in (cls',trail')
+
+backtrack :: forall t. Set.Set (t, TrailMix) -> Set.Set (t, TrailMix)
+backtrack trail =
+  case Set.minView trail of
+    Just ((p,Deduced), tt) -> backtrack tt
+    _ -> trail
+
+dpli :: forall atomic pf. (PropositionalFormula pf atomic, Ord pf) =>
+        Set.Set (Set.Set pf) -> Set.Set (pf, TrailMix) -> Failing Bool
+dpli cls trail =
+  let (cls', trail') = unit_propagate (cls, trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView trail of
+      Just ((p,Guessed), tt) -> dpli cls (Set.insert ((.~.) p, Deduced) tt)
+      _ -> Success False
+  else
+      case unassigned cls (trail' :: Set.Set (pf, TrailMix)) of
+        s | Set.null s -> Success True
+        ps -> case maximize' (posneg_count cls') ps of
+                Just p -> dpli cls (Set.insert (p :: pf, Guessed) trail')
+                Nothing -> Failure ["dpli"]
+
+dplisat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplisat fm = dpli (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplitaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplitaut fm = dplisat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- With simple non-chronological backjumping and learning.                   
+-- ------------------------------------------------------------------------- 
+
+backjump :: forall a. (Negatable a, Ord a) =>
+            Set.Set (Set.Set a) -> a -> Set.Set (a, TrailMix) -> Set.Set (a, TrailMix)
+backjump cls p trail =
+  case Set.minView (backtrack trail) of
+    Just ((q,Guessed), tt) ->
+        let (cls',trail') = unit_propagate (cls, Set.insert (p,Guessed) tt) in
+        if Set.member Set.empty cls' then backjump cls p tt else trail
+    _ -> trail
+
+dplb :: forall a. (Negatable a, Ord a) =>
+        Set.Set (Set.Set a) -> Set.Set (a, TrailMix) -> Failing Bool
+dplb cls trail =
+  let (cls',trail') = unit_propagate (cls,trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView (backtrack trail) of
+      Just ((p,Guessed), tt) ->
+        let trail'' = backjump cls p tt in
+        let declits = Set.filter (\ (_,d) -> d == Guessed) trail'' in
+        let conflict = Set.insert ((.~.) p) (Set.map ((.~.) . fst) declits) in
+        dplb (Set.insert conflict cls) (Set.insert ((.~.) p,Deduced) trail'')
+      _ -> Success False
+  else
+    case unassigned cls trail' of
+      s | Set.null s -> Success True
+      ps -> case maximize' (posneg_count cls') ps of
+              Just p -> dplb cls (Set.insert (p,Guessed) trail')
+              Nothing -> Failure ["dpib"]
+            
+dplbsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplbsat fm = dplb (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplbtaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplbtaut fm = dplbsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test03 = TestList [TestCase (assertEqual "dplitaut(prime 101)" (Success True) (dplitaut(prime 101 :: Formula (Atom N)))),
+                   TestCase (assertEqual "dplbtaut(prime 101)" (Success True) (dplbtaut(prime 101 :: Formula (Atom N))))]
diff --git a/testdata/split-expected/Data/Logic/Harrison/DefCNF.hs b/testdata/split-expected/Data/Logic/Harrison/DefCNF.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/DefCNF.hs
@@ -0,0 +1,160 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DefCNF
+    {- ( Atom
+    , NumAtom(ma, ai)
+    , defcnfs
+    , defcnf1
+    , defcnf2
+    , defcnf3
+    ) -} where
+
+import Data.Logic.Classes.Combine ((.&.), (.<=>.), (.|.), BinOp(..), Combination(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Propositional (overatoms, PropositionalFormula(foldPropositional))
+import Data.Logic.Harrison.Prop (cnf, nenf, simpcnf)
+import Data.Logic.Harrison.PropExamples (N)
+import qualified Data.Map as Map (elems, empty, insert, lookup, Map)
+import qualified Data.Set.Extra as Set (Set, unions)
+
+-- ========================================================================= 
+-- Definitional CNF.                                                         
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+{-
+START_INTERACTIVE;;
+cnf <<p <=> (q <=> r)>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Make a stylized variable and update the index.                            
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P a
+
+class NumAtom atom where
+    ma :: N -> atom
+    ai :: atom -> N
+
+instance NumAtom (Atom N) where
+    ma = P
+    ai (P n) = n
+
+mkprop :: forall pf atom. (PropositionalFormula pf atom, NumAtom atom) => N -> (pf, N)
+mkprop n = (atomic (ma n :: atom), n + 1)
+
+-- ------------------------------------------------------------------------- 
+-- Core definitional CNF procedure.                                          
+-- ------------------------------------------------------------------------- 
+
+maincnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+maincnf trip@(fm, _defs, _n) =
+    foldPropositional co tf at fm
+    where
+      co (BinOp p (:&:) q) = defstep (.&.) (p,q) trip
+      co (BinOp p (:|:) q) = defstep (.|.) (p,q) trip
+      co (BinOp p (:<=>:) q) = defstep (.<=>.) (p,q) trip
+      co (BinOp _ (:=>:) _) = trip
+      co ((:~:) _) = trip
+      tf _ = trip
+      at _ = trip
+
+defstep :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf -> pf -> pf) -> (pf, pf) -> (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+defstep op (p,q) (_fm, defs, n) =
+  let (fm1,defs1,n1) = maincnf (p,defs,n) in
+  let (fm2,defs2,n2) = maincnf (q,defs1,n1) in
+  let fm' = op fm1 fm2 in
+  case Map.lookup fm' defs2 of
+    Just _ -> (fm', defs2, n2)
+    Nothing -> let (v,n3) = mkprop n2 in (v, Map.insert v (v .<=>. fm') defs2,n3)
+
+-- ------------------------------------------------------------------------- 
+-- Make n large enough that "v_m" won't clash with s for any m >= n          
+-- ------------------------------------------------------------------------- 
+
+max_varindex :: NumAtom atom =>  atom -> Int -> Int
+max_varindex atom n = max n (ai atom)
+
+-- ------------------------------------------------------------------------- 
+-- Overall definitional CNF.                                                 
+-- ------------------------------------------------------------------------- 
+
+mk_defcnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) =>
+             ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)) -> pf -> Set.Set (Set.Set lit)
+mk_defcnf fn fm =
+  let fm' = nenf fm in
+  let n = 1 + overatoms max_varindex fm' 0 in
+  let (fm'',defs,_) = fn (fm',Map.empty,n) in
+  let (deflist {- :: [pf]-}) = Map.elems defs in
+  Set.unions (simpcnf fm'' : map simpcnf deflist)
+
+defcnf1 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf1 fm = cnf (mk_defcnf maincnf fm :: Set.Set (Set.Set lit))
+
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf1 <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version tweaked to exploit initial structure.                             
+-- ------------------------------------------------------------------------- 
+
+subcnf :: (PropositionalFormula pf atom, NumAtom atom) =>
+          ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int))
+       -> (pf -> pf -> pf)
+       -> pf
+       -> pf
+       -> (pf, Map.Map pf pf, Int)
+       -> (pf, Map.Map pf pf, Int)
+subcnf sfn op p q (_fm,defs,n) =
+  let (fm1,defs1,n1) = sfn (p,defs,n) in
+  let (fm2,defs2,n2) = sfn (q,defs1,n1) in
+  (op fm1 fm2, defs2, n2)
+
+orcnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+orcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:|:) q) = subcnf orcnf (.|.) p q trip
+      co _ = maincnf trip
+
+andcnf :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> orcnf trip) (\ _ -> orcnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf (.&.) p q trip
+      co _ = orcnf trip
+
+defcnfs :: (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+defcnfs fm = mk_defcnf andcnf fm
+
+defcnf2 :: forall pf lit atom.(PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf2 fm = cnf (defcnfs fm :: Set.Set (Set.Set lit))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version that guarantees 3-CNF.                                            
+-- ------------------------------------------------------------------------- 
+
+andcnf3 :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf3 trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf3 (.&.) p q trip
+      co _ = maincnf trip
+
+defcnf3 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf3 fm = cnf (mk_defcnf andcnf3 fm :: Set.Set (Set.Set lit))
diff --git a/testdata/split-expected/Data/Logic/Harrison/Equal.hs b/testdata/split-expected/Data/Logic/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Equal.hs
@@ -0,0 +1,331 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, RankNTypes, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Equal
+{-  ( function_congruence
+    , equalitize
+    ) -} where
+
+-- ========================================================================= 
+-- First order logic with equality.                                          
+--                                                                           
+-- Copyright (co) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine ((∧), (⇒))
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Equals (AtomEq(..), applyEq, (.=.), PredicateName(..), funcsAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), (∀))
+import Data.Logic.Classes.Formula (Formula(atomic, foldAtoms))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (atom_union)
+import Data.Logic.Harrison.Lib ((∅))
+-- import Data.Logic.Harrison.Skolem (functions)
+import qualified Data.Set as Set
+import Data.String (IsString(fromString))
+
+-- is_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Bool
+-- is_eq = foldFirstOrder (\ _ _ _ -> False) (\ _ -> False) (\ _ -> False) (foldAtomEq (\ _ _ -> False) (\ _ -> False) (\ _ _ -> True))
+-- 
+-- mk_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+-- mk_eq = (.=.)
+-- 
+-- dest_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing (term, term)
+-- dest_eq fm =
+--     foldFirstOrder (\ _ _ _ -> err) (\ _ -> err) (\ _ -> err) at fm
+--     where
+--       at = foldAtomEq (\ _ _ -> err) (\ _ -> err) (\ s t -> Success (s, t))
+--       err = Failure ["dest_eq: not an equation"]
+-- 
+-- lhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- lhs eq = dest_eq eq >>= return . fst
+-- rhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- rhs eq = dest_eq eq >>= return . snd
+
+-- ------------------------------------------------------------------------- 
+-- The set of predicates in a formula.                                       
+-- ------------------------------------------------------------------------- 
+
+predicates :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) => formula -> Set.Set (PredicateName p)
+predicates fm =
+    atom_union pair fm
+    where -- pair :: atom -> Set.Set (p, Int)
+          pair = foldAtomEq (\ p a -> Set.singleton (Named p (maybe (length a)
+                                                                    (\ n -> if n /= length a then n else error "arity mismatch")
+                                                                    (arity p))))
+                            (\ x -> Set.singleton (Named (fromBool x) 0))
+                            (\ _ _ -> Set.singleton Equals)
+
+{-
+-- | Traverse a formula and pass all (predicates, arity) pairs to a function.
+-- To collect
+foldPredicates :: forall formula atom term v p r. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) =>
+                  (PredicateName p -> Maybe Int -> r -> r) -> formula -> r -> r
+foldPredicates f fm acc =
+    foldFirstOrder qu co tf at fm
+    where
+      fold = foldPredicates f
+      qu _ _ p = fold p acc
+      co (BinOp l _ r) = fold r (fold l acc)
+      co ((:~:) p) = fold p acc
+      tf x = fold (fromBool x) acc
+      at = foldAtomEq ap tf eq
+      ap p _ = f (Name p) (arity p) acc
+      eq _ _ = f Equals (Just 2) acc
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Code to generate equality axioms for functions.                           
+-- ------------------------------------------------------------------------- 
+
+function_congruence :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f) =>
+                       (f, Int) -> Set.Set fof
+function_congruence (_,0) = (∅)
+function_congruence (f,n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x :: [v]
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y :: [v]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = fApp f args_x .=. fApp f args_y
+  
+-- ------------------------------------------------------------------------- 
+-- And for predicates.                                                       
+-- ------------------------------------------------------------------------- 
+
+predicate_congruence :: (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord p) =>
+                        PredicateName p -> Set.Set fof
+predicate_congruence Equals = Set.empty
+predicate_congruence (Named _ 0) = Set.empty
+predicate_congruence (Named p n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = atomic (applyEq p args_x) ⇒ atomic (applyEq p args_y)
+
+-- ------------------------------------------------------------------------- 
+-- Hence implement logic with equality just by adding equality "axioms".     
+-- ------------------------------------------------------------------------- 
+
+equivalence_axioms :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord fof) => Set.Set fof
+equivalence_axioms =
+    Set.fromList
+    [(∀) "x" (x .=. x),
+     (∀) "x" ((∀) "y" ((∀) "z" (x .=. y ∧ x .=. z ⇒ y .=. z)))]
+    where
+      x :: term
+      x = vt (fromString "x")
+      y :: term
+      y = vt (fromString "y")
+      z :: term
+      z = vt (fromString "z")
+
+equalitize :: forall formula atom term v p f. (FirstOrderFormula formula atom v, Formula formula atom, AtomEq atom p term, Ord p, Show p, Term term v f, Ord formula, Ord f) =>
+              formula -> formula
+equalitize fm =
+    if not (Set.member Equals allpreds)
+    then fm
+    else foldr1 (∧) (Set.toList axioms) ⇒ fm
+    where
+      axioms = Set.fold (Set.union . function_congruence) (Set.fold (Set.union . predicate_congruence) equivalence_axioms preds) (functions' funcsAtomEq' fm)
+      funcsAtomEq' :: atom -> Set.Set (f, Int)
+      funcsAtomEq' = funcsAtomEq
+      allpreds = predicates fm
+      preds = Set.delete Equals allpreds
+
+functions' :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions' fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- Other variants not mentioned in book.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+{- ************
+
+(meson ** equalitize)
+ <<(forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x * 1 = x) /\
+   (forall x. x * x = 1)
+   ==> forall x y. x * y  = y * x>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With symmetry at leaves and one-sided congruences (Size = 16, 54659 s).   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x. x = x) /\
+   (forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x y z. =((x * y) * z,x * (y * z))) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x = 1 * x) /\
+   (forall x. i(x) * x = 1) /\
+   (forall x. 1 = i(x) * x) /\
+   (forall x y. x = y ==> i(x) = i(y)) /\
+   (forall x y z. x = y ==> x * z = y * z) /\
+   (forall x y z. x = y ==> z * x = z * y) /\
+   (forall x y z. x = y /\ y = z ==> x = z)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Newer version of stratified equalities.                                   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cchain(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cchain(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall x x' u. x = x' /\ achain(i(x'),u) ==> cchain(i(x),u)) /\
+   (forall x x' y y' u.
+        x = x' /\ y = y' /\ achain(x' * y',u) ==> cchain(x * y,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cong(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cong(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Showing congruence closure.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm = equalitize
+ <<forall c. f(f(f(f(f(c))))) = c /\ f(f(f(c))) = c ==> f(c) = c>>;;
+
+time meson fm;;
+
+let fm =
+ <<axiom(f(f(f(f(f(c))))),c) /\
+   axiom(c,f(f(f(f(f(c)))))) /\
+   axiom(f(f(f(c))),c) /\
+   axiom(c,f(f(f(c)))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t) /\
+   (forall x y. x = y ==> cong(f(x),f(y)))
+   ==> f(c) = c>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- With stratified equalities.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x. eqA (x,x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqT (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y z. eqA (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With transitivity chains...                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y. eqA (w,x) ==> eqC (w * y,x * y)) /\
+   (forall w x y. eqC (w,x) ==> eqC (w * y,x * y)) /\
+   (forall x y z. eqA (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqC (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1) \/ eqC (x * i(x),1)>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Enforce canonicity (proof size = 20).                                     
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eq1(x * (y * z),(x * y) * z)) /\
+   (forall x y z. eq1((x * y) * z,x * (y * z))) /\
+   (forall x. eq1(1 * x,x)) /\
+   (forall x. eq1(x,1 * x)) /\
+   (forall x. eq1(i(x) * x,1)) /\
+   (forall x. eq1(1,i(x) * x)) /\
+   (forall x y z. eq1(x,y) ==> eq1(x * z,y * z)) /\
+   (forall x y z. eq1(x,y) ==> eq1(z * x,z * y)) /\
+   (forall x y z. eq1(x,y) /\ eq2(y,z) ==> eq2(x,z)) /\
+   (forall x y. eq1(x,y) ==> eq2(x,y))
+   ==> forall x. eq2(x,i(x))>>;;
+
+time meson fm;;
+
+***************** -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/FOL.hs b/testdata/split-expected/Data/Logic/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/FOL.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.FOL
+    ( eval
+    , list_disj
+    , list_conj
+    , var
+    , fv
+    -- , fv'
+    , subst
+    -- , subst'
+    , generalize
+    ) where
+
+import Data.Logic.Classes.Apply (Apply(..), apply)
+import Data.Logic.Classes.Atom (Atom(allVariables, substitute))
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants (fromBool), true, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term(vt), fvt)
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (on_atoms)
+import Data.Logic.Harrison.Lib ((|->), setAny)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Prelude hiding (pred)
+
+-- =========================================================================
+-- Basic stuff for first order logic.                                       
+--                                                                          
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.) 
+-- =========================================================================
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: FirstOrderFormula formula atom v => formula -> (atom -> Bool) -> Bool
+eval fm v =
+    foldFirstOrder qu co id at fm
+    where
+      qu _ _ p = eval p v
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at = v
+
+list_conj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (FirstOrderFormula formula atom v, Ord formula) =>
+          Set.Set formula -> (atom -> Bool) -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+-- -------------------------------------------------------------------------
+-- Special case of applying a subfunction to the top *terms*.               
+-- -------------------------------------------------------------------------
+
+on_formula :: forall fol atom term v p. (FirstOrderFormula fol atom v, Apply atom p term) => (term -> term) -> fol -> fol
+on_formula f = on_atoms (foldApply (\ p ts -> atomic (apply p (map f ts) :: atom)) fromBool)
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of terms.                                                         
+-- ------------------------------------------------------------------------- 
+
+{-
+let is_const_name s = forall numeric (explode s) or s = "nil";;
+
+let rec parse_atomic_term vs inp =
+  match inp with
+    [] -> failwith "term expected"
+  | "("::rest -> parse_bracketed (parse_term vs) ")" rest
+  | "-"::rest -> papply (fun t -> Fn("-",[t])) (parse_atomic_term vs rest)
+  | f::"("::")"::rest -> Fn(f,[]),rest
+  | f::"("::rest ->
+      papply (fun args -> Fn(f,args))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | a::rest ->
+      (if is_const_name a & not(mem a vs) then Fn(a,[]) else Var a),rest
+
+and parse_term vs inp =
+  parse_right_infix "::" (fun (e1,e2) -> Fn("::",[e1;e2]))
+    (parse_right_infix "+" (fun (e1,e2) -> Fn("+",[e1;e2]))
+       (parse_left_infix "-" (fun (e1,e2) -> Fn("-",[e1;e2]))
+          (parse_right_infix "*" (fun (e1,e2) -> Fn("*",[e1;e2]))
+             (parse_left_infix "/" (fun (e1,e2) -> Fn("/",[e1;e2]))
+                (parse_left_infix "^" (fun (e1,e2) -> Fn("^",[e1;e2]))
+                   (parse_atomic_term vs)))))) inp;;
+
+let parset = make_parser (parse_term []);;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas.                                                      
+-- ------------------------------------------------------------------------- 
+
+let parse_infix_atom vs inp =       
+  let tm,rest = parse_term vs inp in
+  if exists (nextin rest) ["="; "<"; "<="; ">"; ">="] then                     
+        papply (fun tm' -> Atom(R(hd rest,[tm;tm'])))                          
+               (parse_term vs (tl rest))                                       
+  else failwith "";;
+                                                               
+let parse_atom vs inp =
+  try parse_infix_atom vs inp with Failure _ ->                                
+  match inp with                                                               
+  | p::"("::")"::rest -> Atom(R(p,[])),rest                                    
+  | p::"("::rest ->
+      papply (fun args -> Atom(R(p,args)))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | p::rest when p <> "(" -> Atom(R(p,[])),rest
+  | _ -> failwith "parse_atom";;
+                                                                               
+let parse = make_parser                                                        
+  (parse_formula (parse_infix_atom,parse_atom) []);;              
+
+-- ------------------------------------------------------------------------- 
+-- Set up parsing of quotations.                                             
+-- ------------------------------------------------------------------------- 
+
+let default_parser = parse;;
+
+let secondary_parser = parset;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printing of terms.                                                        
+-- ------------------------------------------------------------------------- 
+{-
+let rec print_term prec fm =
+  match fm with
+    Var x -> print_string x
+  | Fn("^",[tm1;tm2]) -> print_infix_term true prec 24 "^" tm1 tm2
+  | Fn("/",[tm1;tm2]) -> print_infix_term true prec 22 " /" tm1 tm2
+  | Fn("*",[tm1;tm2]) -> print_infix_term false prec 20 " *" tm1 tm2
+  | Fn("-",[tm1;tm2]) -> print_infix_term true prec 18 " -" tm1 tm2
+  | Fn("+",[tm1;tm2]) -> print_infix_term false prec 16 " +" tm1 tm2
+  | Fn("::",[tm1;tm2]) -> print_infix_term false prec 14 "::" tm1 tm2
+  | Fn(f,args) -> print_fargs f args
+
+and print_fargs f args =
+  print_string f;
+  if args = [] then () else
+   (print_string "(";
+    open_box 0;
+    print_term 0 (hd args); print_break 0 0;
+    do_list (fun t -> print_string ","; print_break 0 0; print_term 0 t)
+            (tl args);
+    close_box();
+    print_string ")")
+
+and print_infix_term isleft oldprec newprec sym p q =
+  if oldprec > newprec then (print_string "("; open_box 0) else ();
+  print_term (if isleft then newprec else newprec+1) p;
+  print_string sym;
+  print_break (if String.sub sym 0 1 = " " then 1 else 0) 0;
+  print_term (if isleft then newprec+1 else newprec) q;
+  if oldprec > newprec then (close_box(); print_string ")") else ();;
+
+let printert tm =
+  open_box 0; print_string "<<|";
+  open_box 0; print_term 0 tm; close_box();
+  print_string "|>>"; close_box();;
+
+#install_printer printert;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas.                                                     
+-- ------------------------------------------------------------------------- 
+
+let print_atom prec (R(p,args)) =
+  if mem p ["="; "<"; "<="; ">"; ">="] & length args = 2
+  then print_infix_term false 12 12 (" "^p) (el 0 args) (el 1 args)
+  else print_fargs p args;;
+
+let print_fol_formula = print_qformula print_atom;;
+
+#install_printer print_fol_formula;;
+
+-- ------------------------------------------------------------------------- 
+-- Examples in the main text.                                                
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+<<forall x y. exists z. x < z /\ y < z>>;;
+
+<<~(forall x. P(x)) <=> exists y. ~P(y)>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Free variables in terms and formulas.                                     
+-- ------------------------------------------------------------------------- 
+
+-- | Return all variables occurring in a formula.
+var :: forall formula atom term v.
+       (FirstOrderFormula formula atom v,
+        Atom atom term v) => formula -> Set.Set v
+var fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.insert x (var p)
+      co ((:~:) p) = var p
+      co (BinOp p _ q) = Set.union (var p) (var q)
+      tf _ = Set.empty
+      at :: atom -> Set.Set v
+      at = allVariables
+
+-- | Return the variables that occur free in a formula.
+fv :: forall formula atom term v.
+      (FirstOrderFormula formula atom v,
+       Atom atom term v) => formula -> Set.Set v
+fv fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.delete x (fv p)
+      co ((:~:) p) = fv p
+      co (BinOp p _ q) = Set.union (fv p) (fv q)
+      tf _ = Set.empty
+      at = allVariables
+
+-- | Return the variables in a propositional formula.
+fv' :: forall formula atom term v. (PropositionalFormula formula atom, Atom atom term v, Ord v) => formula -> Set.Set v
+fv' fm =
+    foldPropositional co tf allVariables fm
+    where
+      co ((:~:) p) = fv' p
+      co (BinOp p _ q) = Set.union (fv' p) (fv' q)
+      tf _ = Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Universal closure of a formula.                                           
+-- ------------------------------------------------------------------------- 
+
+generalize :: (FirstOrderFormula formula atom v, Atom atom term v) => formula -> formula
+generalize fm = Set.fold for_all fm (fv fm)
+
+-- ------------------------------------------------------------------------- 
+-- Substitution in formulas, with variable renaming.                         
+-- ------------------------------------------------------------------------- 
+
+subst :: (FirstOrderFormula formula atom v,
+          Term term v f,
+          Atom atom term v) =>
+         Map.Map v term -> formula -> formula
+subst env fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x p = quant op x' (subst ((x |-> vt x') env) p)
+          where
+            x' = if setAny (\ y -> Set.member x (fvt (fromMaybe (vt y) (Map.lookup y env)))) (Set.delete x (fv p))
+                 then variant x (fv (subst (Map.delete x env) p))
+                 else x
+      co ((:~:) p) = ((.~.) (subst env p))
+      co (BinOp p op q) = binop (subst env p) op (subst env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+subst' :: (PropositionalFormula formula atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          Map.Map v term -> formula -> formula
+subst' env fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = ((.~.) (subst' env p))
+      co (BinOp p op q) = binop (subst' env p) op (subst' env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+{-
+-- |Replace each free occurrence of variable old with term new.
+substitute :: forall formula atom term v f. (FirstOrderFormula formula atom v, Term term v f) => v -> term -> (atom -> formula) -> formula -> formula
+substitute old new atom formula =
+    foldTerm (\ new' -> if old == new' then formula else substitute' formula)
+             (\ _ _ -> substitute' formula)
+             new
+    where
+      substitute' =
+          foldFirstOrder -- If the old variable appears in a quantifier
+                -- we can stop doing the substitution.
+                (\ q v f' -> quant q v (if old == v then f' else substitute' f'))
+                (\ cm -> case cm of
+                           ((:~:) f') -> combine ((:~:) (substitute' f'))
+                           (BinOp f1 op f2) -> combine (BinOp (substitute' f1) op (substitute' f2)))
+                fromBool
+                atom
+-}
+{-
+    substitute old new atom formula
+    where 
+      atom = foldAtomEq (\ p ts -> pApp p (map st ts)) fromBool (\ t1 t2 -> st t1 .=. st t2)
+      st :: term -> term
+      st t = foldTerm sv (\ func ts -> fApp func (map st ts)) t
+      sv v = if v == old then new else vt v
+-}
+
diff --git a/testdata/split-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs b/testdata/split-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,211 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.FirstOrder
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Negate ((.~.))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: FirstOrderFormula formula atom v => formula -> formula
+antecedent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = err
+      err = error "antecedent"
+
+consequent :: FirstOrderFormula formula atom v => formula -> formula
+consequent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = err
+      err = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+on_atoms f fm =
+    foldFirstOrder qu co tf at fm
+    where 
+      qu op v fm' = quant op v (on_atoms f fm')
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at = f
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: FirstOrderFormula formula atom v => (atom -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldFirstOrder qu co tf pr fm
+    where
+      qu _ _ p = over_atoms f p b
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      pr atom = f atom b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (FirstOrderFormula formula atom v, Ord b) => (atom -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-expected/Data/Logic/Harrison/Formulas/Propositional.hs b/testdata/split-expected/Data/Logic/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,208 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.Propositional
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+--import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: PropositionalFormula formula atomic => formula -> formula
+antecedent formula =
+    foldPropositional c (error "antecedent") (error "antecedent") formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = error "antecedent"
+
+consequent :: PropositionalFormula formula atomic => formula -> formula
+consequent formula =
+    foldPropositional c (error "consequent") (error "consequent") formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: PropositionalFormula formula atomic => (atomic -> formula) -> formula -> formula
+on_atoms f fm =
+    foldPropositional co tf at fm
+    where 
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at x = f x
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: (PropositionalFormula formula atomic) => (atomic -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      at x = f x b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (PropositionalFormula formula atomic, Ord b) => (atomic -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-expected/Data/Logic/Harrison/Herbrand.hs b/testdata/split-expected/Data/Logic/Harrison/Herbrand.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Herbrand.hs
@@ -0,0 +1,309 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Harrison.Herbrand where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom(substitute, freeVariables))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (fApp, Term)
+import Data.Logic.Harrison.DP (dpll)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib')
+import Data.Logic.Harrison.Normal (trivial)
+import Data.Logic.Harrison.Prop (eval, simpcnf, simpdnf)
+import Data.Logic.Harrison.Skolem (functions, runSkolem, skolemize)
+import qualified Data.Map as Map (fromList, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, insert, map, minView, null, partition, Set, singleton, size, toList, union)
+import Data.String (IsString(..))
+
+-- ========================================================================= 
+-- Relation between FOL and propositonal logic; Herbrand theorem.            
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Propositional valuation.                                                  
+-- ------------------------------------------------------------------------- 
+
+pholds :: (PropositionalFormula formula atom, Ord atom) => (Map.Map atom Bool) -> formula -> Bool
+pholds d fm = eval fm d
+
+-- ------------------------------------------------------------------------- 
+-- Get the constants for Herbrand base, adding nullary one if necessary.     
+-- ------------------------------------------------------------------------- 
+
+herbfuns :: forall pf atom term v f. (PropositionalFormula pf atom, Formula pf atom, Atom atom term v, Term term v f, IsString f, Ord f) =>
+            (atom -> Set.Set (f, Int))
+         -> pf
+         -> (Set.Set (f, Int), Set.Set (f, Int))
+herbfuns fa fm =
+  let (cns,fns) = Set.partition (\ (_,ar) -> ar == 0) (functions fa fm) in
+  if Set.null cns then (Set.singleton (fromString "c",0),fns) else (cns,fns)
+
+-- ------------------------------------------------------------------------- 
+-- Enumeration of ground terms and m-tuples, ordered by total fns.           
+-- ------------------------------------------------------------------------- 
+
+groundterms :: forall term v f. (Term term v f) =>
+               Set.Set term -> Set.Set (f, Int) -> Int -> Set.Set term
+groundterms cntms _ 0 = cntms
+groundterms cntms funcs n =
+    Set.fold terms Set.empty funcs
+    where
+      terms (f,m) l = Set.union (Set.map (fApp f) (groundtuples cntms funcs (n - 1) m)) l
+
+groundtuples :: forall term v f. (Term term v f) =>
+                Set.Set term -> Set.Set (f, Int) -> Int -> Int -> Set.Set [term]
+groundtuples _ _ 0 0 = Set.singleton []
+groundtuples _ _ _ 0 = Set.empty
+groundtuples cntms funcs n m =
+    Set.fold tuples Set.empty (Set.fromList [0 .. n])
+    where 
+      tuples k l = Set.union (allpairs (:) (groundterms cntms funcs k) (groundtuples cntms funcs (n - k) (m - 1))) l
+
+-- ------------------------------------------------------------------------- 
+-- Iterate modifier "mfn" over ground terms till "tfn" fails.                
+-- ------------------------------------------------------------------------- 
+
+herbloop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v) =>
+            (Set.Set (Set.Set lit) -> (lit -> lit) -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit))
+         -> (Set.Set (Set.Set lit) -> Failing Bool)
+         -> Set.Set (Set.Set lit)
+         -> Set.Set term
+         -> Set.Set (f, Int)
+         -> [v]
+         -> Int
+         -> Set.Set (Set.Set lit)
+         -> Set.Set [term]
+         -> Set.Set [term]
+         -> Failing (Set.Set [term])
+herbloop mfn tfn fl0 cntms funcs fvs n fl tried tuples =
+{-
+  print_string(string_of_int(length tried) ++ " ground instances tried; " ++
+               string_of_int(length fl) ++ " items in list")
+  print_newline();
+-}
+  case Set.minView tuples of
+    Nothing ->
+          let newtups = groundtuples cntms funcs n (length fvs) in
+          herbloop mfn tfn fl0 cntms funcs fvs (n + 1) fl tried newtups
+    Just (tup, tups) ->
+        let fpf' = Map.fromList (zip fvs tup) in
+        let fl' = mfn fl0 (subst' fpf') fl in
+        case tfn fl' of
+          Failure msgs -> Failure msgs
+          Success x ->
+              if not x
+              then Success (Set.insert tup tried)
+              else herbloop mfn tfn fl0 cntms funcs fvs n fl' (Set.insert tup tried) tups
+
+subst' :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => Map.Map v term -> lit -> lit
+subst' env fm =
+    mapAtoms (atomic . substitute') fm
+    where substitute' :: atom -> atom
+          substitute' = substitute env
+
+-- ------------------------------------------------------------------------- 
+-- Hence a simple Gilmore-type procedure.                                    
+-- ------------------------------------------------------------------------- 
+
+gilmore_loop :: (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit)
+             -> Set.Set term
+             -> Set.Set (f, Int)
+             -> [v]
+             -> Int
+             -> Set.Set (Set.Set lit)
+             -> Set.Set [term]
+             -> Set.Set [term]
+             -> Failing (Set.Set [term])
+gilmore_loop =
+    herbloop mfn (Success . not . Set.null)
+    where
+      mfn djs0 ifn djs = Set.filter (not . trivial) (distrib' (Set.map (Set.map ifn) djs0) djs)
+
+gilmore :: forall fof pf atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            Atom atom term v,
+            IsString f,
+            Ord pf) =>
+           (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+gilmore fa fm =
+  let sfm = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union s (freeVariables a)) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+  gilmore_loop (simpdnf sfm :: Set.Set (Set.Set pf)) cntms funcs (fvs) 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- First example and a little tracing.                                       
+-- ------------------------------------------------------------------------- 
+{-
+test01 =
+    let fm = exists "x" (for_all "y" (pApp "p" [vt "x"] .=>. pApp "p" [vt "y"]))
+        sfm = skolemize ((.~.) fm) in
+    TestList [TestCase (assertEqual "gilmore 1" 2 (gilmore fm))]
+
+START_INTERACTIVE;;
+gilmore <<exists x. forall y. P(x) ==> P(y)>>;;
+
+let sfm = skolemize(Not <<exists x. forall y. P(x) ==> P(y)>>);;
+
+-- ------------------------------------------------------------------------- 
+-- Quick example.                                                            
+-- ------------------------------------------------------------------------- 
+
+let p24 = gilmore
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x))
+   ==> (exists x. P(x) /\ R(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Slightly less easy example.                                               
+-- ------------------------------------------------------------------------- 
+
+let p45 = gilmore
+ <<(forall x. P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))
+              ==> (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==> L(y)) /\
+                      (forall y. G(y) /\ H(x,y) ==> J(x,y)))
+   ==> (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Apparently intractable example.                                           
+-- ------------------------------------------------------------------------- 
+
+{-
+
+let p20 = gilmore
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+-}
+
+
+-- ------------------------------------------------------------------------- 
+-- The Davis-Putnam procedure for first order logic.                         
+-- ------------------------------------------------------------------------- 
+
+dp_mfn :: (Ord b, Ord a) =>
+          Set.Set (Set.Set a)
+       -> (a -> b)
+       -> Set.Set (Set.Set b)
+       -> Set.Set (Set.Set b)
+dp_mfn cjs0 ifn cjs = Set.union (Set.map (Set.map ifn) cjs0) cjs
+
+dp_loop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+           Set.Set (Set.Set lit)
+        -> Set.Set term
+        -> Set.Set (f, Int)
+        -> [v]
+        -> Int
+        -> Set.Set (Set.Set lit)
+        -> Set.Set [term]
+        -> Set.Set [term]
+        -> Failing (Set.Set [term])
+dp_loop = herbloop dp_mfn dpll
+
+davisputnam :: forall fof atom term v lit f.
+               (FirstOrderFormula fof atom v,
+                PropositionalFormula lit atom,
+                Literal lit atom,
+                Term term v f,
+                Atom atom term v,
+                IsString f,
+                Ord lit) =>
+               (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam fa fm =
+  let (sfm :: lit) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c [] :: term) consts in
+  dp_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- Show how much better than the Gilmore procedure this can be.              
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+let p20 = davisputnam
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Try to cut out useless instantiations in final result.                    
+-- ------------------------------------------------------------------------- 
+
+dp_refine :: (Literal lit atom, Atom atom term v, Term term v f) =>
+             Set.Set (Set.Set lit) -> [v] -> Set.Set [term] -> Set.Set [term] -> Failing (Set.Set [term])
+dp_refine cjs0 fvs dknow need =
+    case Set.minView dknow of
+      Nothing -> Success need
+      Just (cl, dknow') ->
+          let mfn = dp_mfn cjs0 . subst' . Map.fromList . zip fvs in
+          dpll (Set.fold mfn Set.empty (Set.union need dknow')) >>= \ flag ->
+          if flag then return (Set.insert cl need) else return need >>=
+          dp_refine cjs0 fvs dknow'
+
+dp_refine_loop :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+                  Set.Set (Set.Set lit)
+               -> Set.Set term
+               -> Set.Set (f, Int)
+               -> [v]
+               -> Int
+               -> Set.Set (Set.Set lit)
+               -> Set.Set [term]
+               -> Set.Set [term]
+               -> Failing (Set.Set [term])
+dp_refine_loop cjs0 cntms funcs fvs n cjs tried tuples =
+    dp_loop cjs0 cntms funcs fvs n cjs tried tuples >>= \ tups ->
+    dp_refine cjs0 fvs tups Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Show how few of the instances we really need. Hence unification!          
+-- ------------------------------------------------------------------------- 
+
+davisputnam' :: forall fof atom term lit v f pf.
+                (FirstOrderFormula fof atom v,
+                 Literal lit atom,
+                 PropositionalFormula pf atom, -- Formula pf atom,
+                 Term term v f,
+                 Atom atom term v,
+                 IsString f) =>
+                (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam' fa fm =
+    let (sfm :: pf) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+    let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+        (consts,funcs) = herbfuns fa sfm in
+    let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+    dp_refine_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+{-
+START_INTERACTIVE;;
+let p36 = davisputnam'
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+                ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+   ==> (forall x. exists y. H(x,y))>>;;
+
+let p29 = davisputnam'
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/LICENSE.txt b/testdata/split-expected/Data/Logic/Harrison/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/LICENSE.txt
@@ -0,0 +1,35 @@
+IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+By downloading, copying, installing or using the software you agree
+to this license.  If you do not agree to this license, do not
+download, install, copy or use the software.
+
+Copyright (c) 2003-2007, John Harrison
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* The name of John Harrison may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/testdata/split-expected/Data/Logic/Harrison/Lib.hs b/testdata/split-expected/Data/Logic/Harrison/Lib.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Lib.hs
@@ -0,0 +1,846 @@
+{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}
+{-# OPTIONS_GHC -Wall -fno-warn-unused-binds #-}
+module Data.Logic.Harrison.Lib
+    ( tests
+    , setAny
+    , setAll
+    -- , itlist2
+    -- , itlist  -- same as foldr with last arguments flipped
+    , tryfind
+    , settryfind
+    -- , end_itlist -- same as foldr1
+    , (|=>)
+    , (|->)
+    , fpf
+    , defined
+    , apply
+    , exists
+    , tryApplyD
+    , allpairs
+    , distrib'
+    , image
+    , optimize
+    , minimize
+    , maximize
+    , optimize'
+    , minimize'
+    , maximize'
+    , can
+    , allsets
+    , allsubsets
+    , allnonemptysubsets
+    , mapfilter
+    , setmapfilter
+    , (∅)
+    ) where
+
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+import Data.Maybe
+import qualified Data.Set as Set
+import Test.HUnit (Test(TestCase, TestList, TestLabel), assertEqual)
+
+(∅) :: Set.Set a
+(∅) = Set.empty
+
+tests :: Test
+tests = TestLabel "Data.Logic.Harrison.Lib" $ TestList [test01]
+
+setAny :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAny f s = Set.member True (Set.map f s)
+
+setAll :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAll f s = not (Set.member False (Set.map f s))
+
+{-
+(* ========================================================================= *)
+(* Misc library functions to set up a nice environment.                      *)
+(* ========================================================================= *)
+
+let identity x = x;;
+
+let ( ** ) = fun f g x -> f(g x);;
+
+(* ------------------------------------------------------------------------- *)
+(* GCD and LCM on arbitrary-precision numbers.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let gcd_num n1 n2 =
+  abs_num(num_of_big_int
+      (Big_int.gcd_big_int (big_int_of_num n1) (big_int_of_num n2)));;
+
+let lcm_num n1 n2 = abs_num(n1 */ n2) // gcd_num n1 n2;;
+
+(* ------------------------------------------------------------------------- *)
+(* A useful idiom for "non contradictory" etc.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let non p x = not(p x);;
+
+(* ------------------------------------------------------------------------- *)
+(* Kind of assertion checking.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let check p x = if p(x) then x else failwith "check";;
+
+(* ------------------------------------------------------------------------- *)
+(* Repetition of a function.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec funpow n f x =
+  if n < 1 then x else funpow (n-1) f (f x);;
+-}
+-- let can f x = try f x; true with Failure _ -> false;;
+can :: (t -> Failing a) -> t -> Bool
+can f x = failing (const True) (const False) (f x)
+
+{-
+let rec repeat f x = try repeat f (f x) with Failure _ -> x;;
+
+(* ------------------------------------------------------------------------- *)
+(* Handy list operations.                                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec (--) = fun m n -> if m > n then [] else m::((m + 1) -- n);;
+
+let rec (---) = fun m n -> if m >/ n then [] else m::((m +/ Int 1) --- n);;
+
+let rec map2 f l1 l2 =
+  match (l1,l2) with
+    [],[] -> []
+  | (h1::t1),(h2::t2) -> let h = f h1 h2 in h::(map2 f t1 t2)
+  | _ -> failwith "map2: length mismatch";;
+
+let rev =
+  let rec rev_append acc l =
+    match l with
+      [] -> acc
+    | h::t -> rev_append (h::acc) t in
+  fun l -> rev_append [] l;;
+
+let hd l =
+  match l with
+   h::t -> h
+  | _ -> failwith "hd";;
+
+let tl l =
+  match l with
+   h::t -> t
+  | _ -> failwith "tl";;
+-}
+
+-- (^) = (++)
+
+itlist :: (a -> b -> b) -> [a] -> b -> b
+-- itlist f xs z = foldr f z xs
+itlist f xs z = foldr f z xs
+
+end_itlist :: (t -> t -> t) -> [t] -> t
+-- end_itlist = foldr1
+end_itlist = foldr1
+
+itlist2 :: (t -> t1 -> Failing t2 -> Failing t2) -> [t] -> [t1] -> Failing t2 -> Failing t2
+itlist2 f l1 l2 b =
+  case (l1,l2) of
+    ([],[]) -> b
+    (h1 : t1, h2 : t2) -> f h1 h2 (itlist2 f t1 t2 b)
+    _ -> Failure ["itlist2"]
+
+{-
+let rec zip l1 l2 =
+  match (l1,l2) with
+        ([],[]) -> []
+      | (h1::t1,h2::t2) -> (h1,h2)::(zip t1 t2)
+      | _ -> failwith "zip";;
+
+let rec forall p l =
+  match l with
+    [] -> true
+  | h::t -> p(h) & forall p t;;
+-}
+exists :: (a -> Bool) -> [a] -> Bool
+exists = any
+{-
+let partition p l =
+    itlist (fun a (yes,no) -> if p a then a::yes,no else yes,a::no) l ([],[]);;
+
+let filter p l = fst(partition p l);;
+
+let length =
+  let rec len k l =
+    if l = [] then k else len (k + 1) (tl l) in
+  fun l -> len 0 l;;
+
+let rec last l =
+  match l with
+    [x] -> x
+  | (h::t) -> last t
+  | [] -> failwith "last";;
+
+let rec butlast l =
+  match l with
+    [_] -> []
+  | (h::t) -> h::(butlast t)
+  | [] -> failwith "butlast";;
+
+let rec find p l =
+  match l with
+      [] -> failwith "find"
+    | (h::t) -> if p(h) then h else find p t;;
+
+let rec el n l =
+  if n = 0 then hd l else el (n - 1) (tl l);;
+
+let map f =
+  let rec mapf l =
+    match l with
+      [] -> []
+    | (x::t) -> let y = f x in y::(mapf t) in
+  mapf;;
+-}
+
+allpairs :: forall a b c. (Ord c) => (a -> b -> c) -> Set.Set a -> Set.Set b -> Set.Set c
+-- allpairs f xs ys = Set.fromList (concatMap (\ z -> map (f z) (Set.toList ys)) (Set.toList xs))
+allpairs f xs ys = Set.fold (\ x zs -> Set.fold (\ y zs' -> Set.insert (f x y) zs') zs ys) Set.empty xs
+
+distrib' :: Ord a => Set.Set (Set.Set a) -> Set.Set (Set.Set a) -> Set.Set (Set.Set a)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+test01 :: Test
+test01 = TestCase $ assertEqual "itlist2" expected input
+    where input = allpairs (,) (Set.fromList [1,2,3]) (Set.fromList [4,5,6])
+          expected = Set.fromList [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)] :: Set.Set (Int, Int)
+
+{-
+let rec distinctpairs l =
+  match l with
+   x::t -> itlist (fun y a -> (x,y) :: a) t (distinctpairs t)
+  | [] -> [];;
+
+let rec chop_list n l =
+  if n = 0 then [],l else
+  try let m,l' = chop_list (n-1) (tl l) in (hd l)::m,l'
+  with Failure _ -> failwith "chop_list";;
+
+let replicate n a = map (fun x -> a) (1--n);;
+
+let rec insertat i x l =
+  if i = 0 then x::l else
+  match l with
+    [] -> failwith "insertat: list too short for position to exist"
+  | h::t -> h::(insertat (i-1) x t);;
+
+let rec forall2 p l1 l2 =
+  match (l1,l2) with
+    [],[] -> true
+  | (h1::t1,h2::t2) -> p h1 h2 & forall2 p t1 t2
+  | _ -> false;;
+
+let index x =
+  let rec ind n l =
+    match l with
+      [] -> failwith "index"
+    | (h::t) -> if Pervasives.compare x h = 0 then n else ind (n + 1) t in
+  ind 0;;
+
+let rec unzip l =
+  match l with
+    [] -> [],[]
+  | (x,y)::t ->
+      let xs,ys = unzip t in x::xs,y::ys;;
+
+(* ------------------------------------------------------------------------- *)
+(* Whether the first of two items comes earlier in the list.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec earlier l x y =
+  match l with
+    h::t -> (Pervasives.compare h y <> 0) &
+            (Pervasives.compare h x = 0 or earlier t x y)
+  | [] -> false;;
+
+(* ------------------------------------------------------------------------- *)
+(* Application of (presumably imperative) function over a list.              *)
+(* ------------------------------------------------------------------------- *)
+
+let rec do_list f l =
+  match l with
+    [] -> ()
+  | h::t -> f(h); do_list f t;;
+
+(* ------------------------------------------------------------------------- *)
+(* Association lists.                                                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare x a = 0 then y else assoc a t
+  | [] -> failwith "find";;
+
+let rec rev_assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare y a = 0 then x else rev_assoc a t
+  | [] -> failwith "find";;
+
+(* ------------------------------------------------------------------------- *)
+(* Merging of sorted lists (maintaining repetitions).                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec merge ord l1 l2 =
+  match l1 with
+    [] -> l2
+  | h1::t1 -> match l2 with
+                [] -> l1
+              | h2::t2 -> if ord h1 h2 then h1::(merge ord t1 l2)
+                          else h2::(merge ord l1 t2);;
+
+(* ------------------------------------------------------------------------- *)
+(* Bottom-up mergesort.                                                      *)
+(* ------------------------------------------------------------------------- *)
+
+let sort ord =
+  let rec mergepairs l1 l2 =
+    match (l1,l2) with
+        ([s],[]) -> s
+      | (l,[]) -> mergepairs [] l
+      | (l,[s1]) -> mergepairs (s1::l) []
+      | (l,(s1::s2::ss)) -> mergepairs ((merge ord s1 s2)::l) ss in
+  fun l -> if l = [] then [] else mergepairs [] (map (fun x -> [x]) l);;
+
+(* ------------------------------------------------------------------------- *)
+(* Common measure predicates to use with "sort".                             *)
+(* ------------------------------------------------------------------------- *)
+
+let increasing f x y = Pervasives.compare (f x) (f y) < 0;;
+
+let decreasing f x y = Pervasives.compare (f x) (f y) > 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* Eliminate repetitions of adjacent elements, with and without counting.    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec uniq l =
+  match l with
+    x::(y::_ as t) -> let t' = uniq t in
+                      if Pervasives.compare x y = 0 then t' else
+                      if t'==t then l else x::t'
+ | _ -> l;;
+
+let repetitions =
+  let rec repcount n l =
+    match l with
+      x::(y::_ as ys) -> if Pervasives.compare y x = 0 then repcount (n + 1) ys
+                  else (x,n)::(repcount 1 ys)
+    | [x] -> [x,n]
+    | [] -> failwith "repcount" in
+  fun l -> if l = [] then [] else repcount 1 l;;
+-}
+
+tryfind :: (t -> Failing a) -> [t] -> Failing a
+tryfind _ [] = Failure ["tryfind"]
+tryfind f l =
+    case l of
+      [] -> Failure ["tryfind"]
+      h : t -> failing (\ _ -> tryfind f t) Success (f h)
+
+settryfind :: (t -> Failing a) -> Set.Set t -> Failing a
+settryfind f l =
+    case Set.minView l of
+      Nothing -> Failure ["settryfind"]
+      Just (h, t) -> failing (\ _ -> settryfind f t) Success (f h)
+
+mapfilter :: (a -> Failing b) -> [a] -> [b]
+mapfilter f l = catMaybes (map (failing (const Nothing) Just . f) l) 
+    -- filter (failing (const False) (const True)) (map f l)
+
+setmapfilter :: Ord b => (a -> Failing b) -> Set.Set a -> Set.Set b
+setmapfilter f s = Set.fold (\ a r -> failing (const r) (`Set.insert` r) (f a)) Set.empty s
+
+-- -------------------------------------------------------------------------
+-- Find list member that maximizes or minimizes a function.                 
+-- -------------------------------------------------------------------------
+
+optimize :: forall a b. (b -> b -> Bool) -> (a -> b) -> [a] -> Maybe a
+optimize _ _ [] = Nothing
+optimize ord f l = Just (fst (foldr1 (\ p@(_,y) p'@(_,y') -> if ord y y' then p else p') (map (\ x -> (x,f x)) l)))
+
+maximize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+maximize f l = optimize (>) f l
+
+minimize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+minimize f l = optimize (<) f l
+
+optimize' :: forall a b. (b -> b -> Bool) -> (a -> b) -> Set.Set a -> Maybe a
+optimize' ord f s = optimize ord f (Set.toList s)
+
+maximize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+maximize' f s = optimize' (>) f s
+
+minimize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+minimize' f s = optimize' (<) f s
+
+-- -------------------------------------------------------------------------
+-- Set operations on ordered lists.                                         
+-- -------------------------------------------------------------------------
+{-
+let setify =
+  let rec canonical lis =
+     match lis with
+       x::(y::_ as rest) -> Pervasives.compare x y < 0 & canonical rest
+     | _ -> true in
+  fun l -> if canonical l then l
+           else uniq (sort (fun x y -> Pervasives.compare x y <= 0) l);;
+
+let union =
+  let rec union l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> l2
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(union t1 t2)
+          else if h1 < h2 then h1::(union t1 l2)
+          else h2::(union l1 t2) in
+  fun s1 s2 -> union (setify s1) (setify s2);;
+
+let intersect =
+  let rec intersect l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> []
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(intersect t1 t2)
+          else if h1 < h2 then intersect t1 l2
+          else intersect l1 t2 in
+  fun s1 s2 -> intersect (setify s1) (setify s2);;
+
+let subtract =
+  let rec subtract l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then subtract t1 t2
+          else if h1 < h2 then h1::(subtract t1 l2)
+          else subtract l1 t2 in
+  fun s1 s2 -> subtract (setify s1) (setify s2);;
+
+let subset,psubset =
+  let rec subset l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> true
+      | (l1,[]) -> false
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then subset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2
+  and psubset l1 l2 =
+    match (l1,l2) with
+        (l1,[]) -> false
+      | ([],l2) -> true
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then psubset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2 in
+  (fun s1 s2 -> subset (setify s1) (setify s2)),
+  (fun s1 s2 -> psubset (setify s1) (setify s2));;
+
+let rec set_eq s1 s2 = (setify s1 = setify s2);;
+
+let insert x s = union [x] s;;
+-}
+
+image :: (Ord b, Ord a) => (a -> b) -> Set.Set a -> Set.Set b
+image f s = Set.map f s
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Union of a family of sets.                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let unions s = setify(itlist (@) s []);;
+
+(* ------------------------------------------------------------------------- *)
+(* List membership. This does *not* assume the list is a set.                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec mem x lis =
+  match lis with
+    [] -> false
+  | (h::t) -> Pervasives.compare x h = 0 or mem x t;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Finding all subsets or all subsets of a given size.                       
+-- ------------------------------------------------------------------------- 
+
+-- allsets :: Ord a => Int -> Set.Set a -> Set.Set (Set.Set a)
+allsets :: forall a b. (Num a, Eq a, Ord b) => a -> Set.Set b -> Set.Set (Set.Set b)
+allsets 0 _ = Set.singleton Set.empty
+allsets m l =
+    case Set.minView l of
+      Nothing -> Set.empty
+      Just (h, t) -> Set.union (Set.map (Set.insert h) (allsets (m - 1) t)) (allsets m t)
+
+allsubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allsubsets s =
+    maybe (Set.singleton Set.empty)
+          (\ (x, t) -> 
+               let res = allsubsets t in
+               Set.union res (Set.map (Set.insert x) res))
+          (Set.minView s)
+
+
+allnonemptysubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allnonemptysubsets s = Set.delete Set.empty (allsubsets s)
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Explosion and implosion of strings.                                       *)
+(* ------------------------------------------------------------------------- *)
+
+let explode s =
+  let rec exap n l =
+     if n < 0 then l else
+      exap (n - 1) ((String.sub s n 1)::l) in
+  exap (String.length s - 1) [];;
+
+let implode l = itlist (^) l "";;
+
+(* ------------------------------------------------------------------------- *)
+(* Timing; useful for documentation but not logically necessary.             *)
+(* ------------------------------------------------------------------------- *)
+
+let time f x =
+  let start_time = Sys.time() in
+  let result = f x in
+  let finish_time = Sys.time() in
+  print_string
+    ("CPU time (user): "^(string_of_float(finish_time -. start_time)));
+  print_newline();
+  result;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Polymorphic finite partial functions via Patricia trees.                 
+--                                                                          
+-- The point of this strange representation is that it is canonical (equal  
+-- functions have the same encoding) yet reasonably efficient on average.   
+--                                                                          
+-- Idea due to Diego Olivier Fernandez Pons (OCaml list, 2003/11/10).       
+-- -------------------------------------------------------------------------
+{-
+data Func a b
+    = Empty
+    | Leaf Int [(a, b)]
+    | Branch Int Int (Func a b) (Func a b)
+
+-- -------------------------------------------------------------------------
+-- Undefined function.                                                      
+-- -------------------------------------------------------------------------
+
+undefinedFunction = Empty
+
+-- -------------------------------------------------------------------------
+-- In case of equality comparison worries, better use this.                 
+-- -------------------------------------------------------------------------
+
+isUndefined Empty = True
+isUndefined _ = False
+
+-- -------------------------------------------------------------------------
+-- Operation analogous to "map" for functions.                                  
+-- -------------------------------------------------------------------------
+
+mapf f t =
+    case t of
+      Empty -> Empty
+      Leaf h l -> Leaf h (map_list f l)
+      Branch p b l r -> Branch p b (mapf f l) (mapf f r)
+    where
+      map_list f l =
+          case l of
+            [] -> []
+            (x,y) : t -> (x, f y) : map_list f t
+
+-- -------------------------------------------------------------------------
+-- Operations analogous to "fold" for lists.                                
+-- -------------------------------------------------------------------------
+
+foldlFn f a t =
+    case t of
+      Empty -> a
+      Leaf h l -> foldl_list f a l
+      Branch p b l r -> foldlFn f (foldlFn f a l) r
+    where
+      foldl_list f a l =
+          case l of
+            [] -> a
+            (x,y) : t -> foldl_list f (f a x y) t
+
+foldrFn f t a =
+    case t of
+      Empty -> a
+      Leaf h l -> foldr_list f l a
+      Branch p b l r -> foldrFn f l (foldrFn f r a)
+    where
+      foldr_list f l a =
+          case l of
+            [] -> a
+            (x, y) : t -> f x y (foldr_list f t a)
+
+-- -------------------------------------------------------------------------
+-- Mapping to sorted-list representation of the graph, domain and range.    
+-- -------------------------------------------------------------------------
+
+graph f = Set.fromList (foldlFn (\ a x y -> (x,y) : a) [] f)
+
+dom f = Set.fromList (foldlFn (\ a x y -> x :a) [] f)
+
+ran f = Set.fromList (foldlFn (\ a x y -> y : a) [] f)
+-}
+
+-- -------------------------------------------------------------------------
+-- Application.                                                             
+-- -------------------------------------------------------------------------
+
+applyD :: Ord k => Map.Map k a -> k -> a -> Map.Map k a
+applyD m k a = Map.insert k a m
+
+apply :: Ord k => Map.Map k a -> k -> Maybe a
+apply m k = Map.lookup k m
+
+tryApplyD :: Ord k => Map.Map k a -> k -> a -> a
+tryApplyD m k d = fromMaybe d (Map.lookup k m)
+
+tryApplyL :: Ord k => Map.Map k [a] -> k -> [a]
+tryApplyL m k = tryApplyD m k []
+{-
+applyD :: (t -> Maybe b) -> (t -> b) -> t -> b
+applyD f d x = maybe (d x) id (f x)
+
+apply :: (t -> Maybe b) -> t -> b
+apply f = applyD f (\ _ -> error "apply")
+
+tryApplyD :: (t -> Maybe b) -> t -> b -> b
+tryApplyD f a d = maybe d id (f a)
+
+tryApplyL :: (t -> Maybe [a]) -> t -> [a]
+tryApplyL f x = tryApplyD f x []
+-}
+
+defined :: Ord t => Map.Map t a -> t -> Bool
+defined = flip Map.member
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Undefinition.                                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let undefine =
+  let rec undefine_list x l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then t
+          else if c < 0 then l else
+          let t' = undefine_list x t in
+          if t' == t then l else ab::t'
+    | [] -> [] in
+  fun x ->
+    let k = Hashtbl.hash x in
+    let rec und t =
+      match t with
+        Leaf(h,l) when h = k ->
+          let l' = undefine_list x l in
+          if l' == l then t
+          else if l' = [] then Empty
+          else Leaf(h,l')
+      | Branch(p,b,l,r) when k land (b - 1) = p ->
+          if k land b = 0 then
+            let l' = und l in
+            if l' == l then t
+            else (match l' with Empty -> r | _ -> Branch(p,b,l',r))
+          else
+            let r' = und r in
+            if r' == r then t
+            else (match r' with Empty -> l | _ -> Branch(p,b,l,r'))
+      | _ -> t in
+    und;;
+
+(* ------------------------------------------------------------------------- *)
+(* Redefinition and combination.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let (|->),combine =
+  let newbranch p1 t1 p2 t2 =
+    let zp = p1 lxor p2 in
+    let b = zp land (-zp) in
+    let p = p1 land (b - 1) in
+    if p1 land b = 0 then Branch(p,b,t1,t2)
+    else Branch(p,b,t2,t1) in
+  let rec define_list (x,y as xy) l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then xy::t
+          else if c < 0 then xy::l
+          else ab::(define_list xy t)
+    | [] -> [xy]
+  and combine_list op z l1 l2 =
+    match (l1,l2) with
+      [],_ -> l2
+    | _,[] -> l1
+    | ((x1,y1 as xy1)::t1,(x2,y2 as xy2)::t2) ->
+          let c = Pervasives.compare x1 x2 in
+          if c < 0 then xy1::(combine_list op z t1 l2)
+          else if c > 0 then xy2::(combine_list op z l1 t2) else
+          let y = op y1 y2 and l = combine_list op z t1 t2 in
+          if z(y) then l else (x1,y)::l in
+  let (|->) x y =
+    let k = Hashtbl.hash x in
+    let rec upd t =
+      match t with
+        Empty -> Leaf (k,[x,y])
+      | Leaf(h,l) ->
+           if h = k then Leaf(h,define_list (x,y) l)
+           else newbranch h t k (Leaf(k,[x,y]))
+      | Branch(p,b,l,r) ->
+          if k land (b - 1) <> p then newbranch p t k (Leaf(k,[x,y]))
+          else if k land b = 0 then Branch(p,b,upd l,r)
+          else Branch(p,b,l,upd r) in
+    upd in
+  let rec combine op z t1 t2 =
+    match (t1,t2) with
+      Empty,_ -> t2
+    | _,Empty -> t1
+    | Leaf(h1,l1),Leaf(h2,l2) ->
+          if h1 = h2 then
+            let l = combine_list op z l1 l2 in
+            if l = [] then Empty else Leaf(h1,l)
+          else newbranch h1 t1 h2 t2
+    | (Leaf(k,lis) as lf),(Branch(p,b,l,r) as br) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z lf l with
+                 Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z lf r with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch k lf p br
+    | (Branch(p,b,l,r) as br),(Leaf(k,lis) as lf) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z l lf with
+                Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z r lf with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch p br k lf
+    | Branch(p1,b1,l1,r1),Branch(p2,b2,l2,r2) ->
+          if b1 < b2 then
+            if p2 land (b1 - 1) <> p1 then newbranch p1 t1 p2 t2
+            else if p2 land b1 = 0 then
+              (match combine op z l1 t2 with
+                 Empty -> r1 | l -> Branch(p1,b1,l,r1))
+            else
+              (match combine op z r1 t2 with
+                 Empty -> l1 | r -> Branch(p1,b1,l1,r))
+          else if b2 < b1 then
+            if p1 land (b2 - 1) <> p2 then newbranch p1 t1 p2 t2
+            else if p1 land b2 = 0 then
+              (match combine op z t1 l2 with
+                 Empty -> r2 | l -> Branch(p2,b2,l,r2))
+            else
+              (match combine op z t1 r2 with
+                 Empty -> l2 | r -> Branch(p2,b2,l2,r))
+          else if p1 = p2 then
+           (match (combine op z l1 l2,combine op z r1 r2) with
+              (Empty,r) -> r | (l,Empty) -> l | (l,r) -> Branch(p1,b1,l,r))
+          else
+            newbranch p1 t1 p2 t2 in
+  (|->),combine;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Special case of point function.                                          
+-- -------------------------------------------------------------------------
+
+(|=>) :: Ord k => k -> a -> Map.Map k a
+x |=> y = Map.fromList [(x, y)]
+
+-- -------------------------------------------------------------------------
+-- Idiom for a mapping zipping domain and range lists.                      
+-- -------------------------------------------------------------------------
+
+(|->) :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
+(|->) a b m = Map.insert a b m
+
+fpf :: Ord a => Map.Map a b -> a -> Maybe b
+fpf m a = Map.lookup a m
+
+-- -------------------------------------------------------------------------
+-- Grab an arbitrary element.                                               
+-- -------------------------------------------------------------------------
+
+choose :: Map.Map k a -> (k, a)
+choose = Map.findMin
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Install a (trivial) printer for finite partial functions.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let print_fpf (f:('a,'b)func) = print_string "<func>";;
+
+#install_printer print_fpf;;
+
+(* ------------------------------------------------------------------------- *)
+(* Related stuff for standard functions.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let valmod a y f x = if x = a then y else f(x);;
+
+let undef x = failwith "undefined function";;
+
+(* ------------------------------------------------------------------------- *)
+(* Union-find algorithm.                                                     *)
+(* ------------------------------------------------------------------------- *)
+
+type ('a)pnode = Nonterminal of 'a | Terminal of 'a * int;;
+
+type ('a)partition = Partition of ('a,('a)pnode)func;;
+
+let rec terminus (Partition f as ptn) a =
+  match (apply f a) with
+    Nonterminal(b) -> terminus ptn b
+  | Terminal(p,q) -> (p,q);;
+
+let tryterminus ptn a =
+  try terminus ptn a with Failure _ -> (a,1);;
+
+let canonize ptn a = fst(tryterminus ptn a);;
+
+let equivalent eqv a b = canonize eqv a = canonize eqv b;;
+
+let equate (a,b) (Partition f as ptn) =
+  let (a',na) = tryterminus ptn a
+  and (b',nb) = tryterminus ptn b in
+  Partition
+   (if a' = b' then f else
+    if na <= nb then
+       itlist identity [a' |-> Nonterminal b'; b' |-> Terminal(b',na+nb)] f
+    else
+       itlist identity [b' |-> Nonterminal a'; a' |-> Terminal(a',na+nb)] f);;
+
+let unequal = Partition undefined;;
+
+let equated (Partition f) = dom f;;
+
+(* ------------------------------------------------------------------------- *)
+(* First number starting at n for which p succeeds.                          *)
+(* ------------------------------------------------------------------------- *)
+
+let rec first n p = if p(n) then n else first (n +/ Int 1) p;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/Meson.hs b/testdata/split-expected/Data/Logic/Harrison/Meson.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Meson.hs
@@ -0,0 +1,547 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Meson where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), negative)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.FOL (generalize, list_conj)
+import Data.Logic.Harrison.Lib (setAll, settryfind)
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf)
+import Data.Logic.Harrison.Prolog (renamerule)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (deepen, unify_literals)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set as Set (delete, empty, fold, insert, map, Set, size, union)
+
+-- =========================================================================
+-- Model elimination procedure (MESON version, based on Stickel's PTTP).     
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Example of naivety of tableau prover.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+tab <<forall a. ~(P(a) /\ (forall y z. Q(y) \/ R(z)) /\ ~P(a))>>;;
+
+tab <<forall a. ~(P(a) /\ ~P(a) /\ (forall y z. Q(y) \/ R(z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The interesting example where tableaux connections make the proof longer. 
+-- Unfortuntely this gets hammered by normalization first...                 
+-- ------------------------------------------------------------------------- 
+
+tab <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+      (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+      (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Generation of contrapositives.                                            
+-- ------------------------------------------------------------------------- 
+
+contrapositives :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => Set.Set fof -> Set.Set (Set.Set fof, fof)
+contrapositives cls =
+    if setAll negative cls then Set.insert (Set.map (.~.) cls,false) base else base
+    where base = Set.map (\ c -> (Set.map (.~.) (Set.delete c cls), c)) cls
+
+-- ------------------------------------------------------------------------- 
+-- The core of MESON: ancestor unification or Prolog-style extension.        
+-- ------------------------------------------------------------------------- 
+
+mexpand :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set (Set.Set fof, fof)
+        -> Set.Set fof
+        -> fof
+        -> ((Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int))
+        -> (Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int)
+mexpand rules ancestors g cont (env,n,k) =
+    if n < 0
+    then Failure ["Too deep"]
+    else case settryfind doAncestor ancestors of
+           Success a -> Success a
+           Failure _ -> settryfind doRule rules
+    where
+      doAncestor a =
+          do mp <- unify_literals env g ((.~.) a)
+             cont (mp, n, k)
+      doRule rule =
+          do mp <- unify_literals env g c
+             mexpand' (mp, n - Set.size asm, k')
+          where
+            mexpand' = Set.fold (mexpand rules (Set.insert g ancestors)) cont asm
+            ((asm, c), k') = renamerule k rule
+
+-- ------------------------------------------------------------------------- 
+-- Full MESON procedure.                                                     
+-- ------------------------------------------------------------------------- 
+
+puremeson :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+             Maybe Int -> fof -> Failing ((Map.Map v term, Int, Int), Int)
+puremeson maxdl fm =
+    deepen f 0 maxdl
+    where
+      f n = mexpand rules Set.empty false return (Map.empty, n, 0)
+      rules = Set.fold (Set.union . contrapositives) Set.empty cls
+      cls = simpcnf (specialize (pnf fm))
+
+meson :: forall m fof atom term f v. (FirstOrderFormula fof atom v, PropositionalFormula fof atom, Literal fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+         Maybe Int -> fof -> SkolemT v term m (Set.Set (Failing ((Map.Map v term, Int, Int), Int)))
+meson maxdl fm =
+    askolemize ((.~.)(generalize fm)) >>=
+    return . Set.map (puremeson maxdl . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+{-
+-- ------------------------------------------------------------------------- 
+-- With repetition checking and divide-and-conquer search.                   
+-- ------------------------------------------------------------------------- 
+
+let rec equal env fm1 fm2 =
+  try unify_literals env (fm1,fm2) == env with Failure _ -> false;;
+
+let expand2 expfn goals1 n1 goals2 n2 n3 cont env k =
+   expfn goals1 (fun (e1,r1,k1) ->
+        expfn goals2 (fun (e2,r2,k2) ->
+                        if n2 + r1 <= n3 + r2 then failwith "pair"
+                        else cont(e2,r2,k2))
+              (e1,n2+r1,k1))
+        (env,n1,k);;
+
+let rec mexpand rules ancestors g cont (env,n,k) =
+  if n < 0 then failwith "Too deep"
+  else if exists (equal env g) ancestors then failwith "repetition" else
+  try tryfind (fun a -> cont (unify_literals env (g,negate a),n,k))
+              ancestors
+  with Failure _ -> tryfind
+    (fun r -> let (asm,c),k' = renamerule k r in
+              mexpands rules (g::ancestors) asm cont
+                       (unify_literals env (g,c),n-length asm,k'))
+    rules
+
+and mexpands rules ancestors gs cont (env,n,k) =
+  if n < 0 then failwith "Too deep" else
+  let m = length gs in
+  if m <= 1 then itlist (mexpand rules ancestors) gs cont (env,n,k) else
+  let n1 = n / 2 in
+  let n2 = n - n1 in
+  let goals1,goals2 = chop_list (m / 2) gs in
+  let expfn = expand2 (mexpands rules ancestors) in
+  try expfn goals1 n1 goals2 n2 (-1) cont env k
+  with Failure _ -> expfn goals2 n1 goals1 n2 n1 cont env k;;
+
+let puremeson fm =
+  let cls = simpcnf(specialize(pnf fm)) in
+  let rules = itlist ((@) ** contrapositives) cls [] in
+  deepen (fun n ->
+     mexpand rules [] False (fun x -> x) (undefined,n,0); n) 0;;
+
+let meson fm =
+  let fm1 = askolemize(Not(generalize fm)) in
+  map (puremeson ** list_conj) (simpdnf fm1);;
+
+-- ------------------------------------------------------------------------- 
+-- The Los problem (depth 20) and the Steamroller (depth 53) --- lengthier.  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+{- ***********
+
+let los = meson
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+let steamroller = meson
+ <<((forall x. P1(x) ==> P0(x)) /\ (exists x. P1(x))) /\
+   ((forall x. P2(x) ==> P0(x)) /\ (exists x. P2(x))) /\
+   ((forall x. P3(x) ==> P0(x)) /\ (exists x. P3(x))) /\
+   ((forall x. P4(x) ==> P0(x)) /\ (exists x. P4(x))) /\
+   ((forall x. P5(x) ==> P0(x)) /\ (exists x. P5(x))) /\
+   ((exists x. Q1(x)) /\ (forall x. Q1(x) ==> Q0(x))) /\
+   (forall x. P0(x)
+              ==> (forall y. Q0(y) ==> R(x,y)) \/
+                  ((forall y. P0(y) /\ S0(y,x) /\
+                              (exists z. Q0(z) /\ R(y,z))
+                              ==> R(x,y)))) /\
+   (forall x y. P3(y) /\ (P5(x) \/ P4(x)) ==> S0(x,y)) /\
+   (forall x y. P3(x) /\ P2(y) ==> S0(x,y)) /\
+   (forall x y. P2(x) /\ P1(y) ==> S0(x,y)) /\
+   (forall x y. P1(x) /\ (P2(y) \/ Q1(y)) ==> ~(R(x,y))) /\
+   (forall x y. P3(x) /\ P4(y) ==> R(x,y)) /\
+   (forall x y. P3(x) /\ P5(y) ==> ~(R(x,y))) /\
+   (forall x. (P4(x) \/ P5(x)) ==> exists y. Q0(y) /\ R(x,y))
+   ==> exists x y. P0(x) /\ P0(y) /\
+                   exists z. Q1(z) /\ R(y,z) /\ R(x,y)>>;;
+
+*************** -}
+
+
+-- ------------------------------------------------------------------------- 
+-- Test it.                                                                  
+-- ------------------------------------------------------------------------- 
+
+let prop_1 = time meson
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let prop_2 = time meson
+ <<~ ~p <=> p>>;;
+
+let prop_3 = time meson
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let prop_4 = time meson
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let prop_5 = time meson
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let prop_6 = time meson
+ <<p \/ ~p>>;;
+
+let prop_7 = time meson
+ <<p \/ ~ ~ ~p>>;;
+
+let prop_8 = time meson
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let prop_9 = time meson
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let prop_10 = time meson
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let prop_11 = time meson
+ <<p <=> p>>;;
+
+let prop_12 = time meson
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let prop_13 = time meson
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let prop_14 = time meson
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let prop_15 = time meson
+ <<p ==> q <=> ~p \/ q>>;;
+
+let prop_16 = time meson
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let prop_17 = time meson
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time meson
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time meson
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time meson
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time meson
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time meson
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time meson
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time meson
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time meson
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time meson
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time meson
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time meson
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time meson
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time meson
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time meson
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time meson
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time meson
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time meson
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time meson
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time meson
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time meson
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time meson
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+let p39 = time meson
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time meson
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time meson
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time meson
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+let p43 = time meson
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+let p44 = time meson
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+let p45 = time meson
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+let p46 = time meson
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time meson
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time meson
+ <<P(f((a),b),f(b,c)) /\
+  P(f(b,c),f(a,c)) /\
+  (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+  ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time meson
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time meson
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time meson
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- ** Amazingly, this still seems non-trivial... in HOL it works at depth 45!
+
+let gilmore_1 = time meson
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+ ** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time meson
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time meson
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time meson
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time meson
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time meson
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This is still a very hard problem
+
+let gilmore_9 = time meson
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Translation of Gilmore procedure using separate definitions.              
+-- ------------------------------------------------------------------------- 
+
+let gilmore_9a = time meson
+ <<(forall x y. P(x,y) <=>
+                forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+   ==> forall x. exists y. forall z.
+             (P(y,x) ==> (P(x,z) ==> P(x,y))) /\
+             (P(x,y) ==> (~P(x,z) ==> P(y,x) /\ P(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time meson
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The "connections make things worse" example once again.                   
+-- ------------------------------------------------------------------------- 
+
+meson <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+        (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+        (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/Normal.hs b/testdata/split-expected/Data/Logic/Harrison/Normal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Normal.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Versions of the normal form functions in Prop for FirstOrderFormula.
+module Data.Logic.Harrison.Normal
+    ( trivial
+    , simpdnf
+    , simpdnf'
+    , simpcnf
+    , simpcnf'
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.FromFirstOrder (fromFirstOrder)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Failing (failing)
+import Data.Logic.Harrison.Lib (allpairs, setAny)
+import Data.Logic.Harrison.Skolem (nnf)
+import qualified Data.Set.Extra as Set (distrib, empty, filter, intersection, isProperSubsetOf, map, null, or, partition, Set, singleton, union)
+import Prelude hiding (negate)
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.  (dsf: now set)
+-- ------------------------------------------------------------------------- 
+
+distrib' :: (Eq formula, Ord formula) => Set.Set (Set.Set formula) -> Set.Set (Set.Set formula) -> Set.Set (Set.Set formula)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (neg, pos) = Set.partition negated lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: (FirstOrderFormula fof atom v, Eq fof, Ord fof) =>
+           fof -> Set.Set (Set.Set fof)
+simpdnf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at _ = Set.singleton (Set.singleton fm)
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf (nnf fm))
+
+purednf :: (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purednf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = Set.singleton (Set.singleton fm)
+      co (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      co (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      co _ = Set.singleton (Set.singleton fm)
+      tf = Set.singleton . Set.singleton . fromBool
+      at _ = Set.singleton (Set.singleton fm)
+
+simpdnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Formula lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+simpdnf' fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at = Set.singleton . Set.singleton . atomic
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf' (nnf fm))
+
+purednf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+purednf' fm =
+    foldFirstOrder (\ _ _ _ -> x) co (\ _ -> x) (\ _ -> x)  fm
+    where
+      -- co :: Combination formula -> Set.Set (Set.Set lit)
+      co (BinOp p (:&:) q) = Set.distrib (purednf' p) (purednf' q)
+      co (BinOp p (:|:) q) = Set.union (purednf' p) (purednf' q)
+      co _ = x
+      -- x :: Set.Set (Set.Set lit)
+      x = failing (const (error "purednf'")) (Set.singleton . Set.singleton) (fromFirstOrder id fm)
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+-- It would be nice to share code this way, but the caller needs to
+-- specify the intermediate lit type, which is a pain.
+-- simpcnf :: forall fof lit atom v. (FirstOrderFormula fof atom v, Ord fof, Literal lit atom v, Eq lit, Ord lit) => fof -> Set.Set (Set.Set fof)
+-- simpcnf fm = Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+
+simpcnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+simpcnf fm =
+    -- Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      at x = Set.singleton (Set.singleton (atomic x))
+      -- Discard any clause that is the proper subset of another clause
+      def = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+
+purecnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purecnf fm = Set.map (Set.map ({-simplify .-} (.~.))) (purednf (nnf ((.~.) fm)))
+
+-- Alternative versions, these should be merged
+
+simpcnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+simpcnf' fm =
+    foldFirstOrder (\ _ _ _ -> cjs') co tf at fm
+    where
+      co _ = cjs'
+      at = Set.singleton . Set.singleton . atomic -- foldAtomEq (\ _ _ -> cjs') tf (\ _ _ -> cjs')
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (`Set.isProperSubsetOf` x) cjs))
+      cjs = Set.filter (not . trivial) (purecnf' (nnf fm)) -- :: Set.Set (Set.Set lit)
+
+-- | CNF: (a | b | c) & (d | e | f)
+purecnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+purecnf' fm = Set.map (Set.map (.~.)) (purednf' (nnf ((.~.) fm)))
diff --git a/testdata/split-expected/Data/Logic/Harrison/Prolog.hs b/testdata/split-expected/Data/Logic/Harrison/Prolog.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Prolog.hs
@@ -0,0 +1,194 @@
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Prolog where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Term (Term(vt))
+import Data.String (IsString (fromString))
+import Data.Logic.Harrison.FOL (fv, subst, list_conj)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- ========================================================================= 
+-- Backchaining procedure for Horn clauses, and toy Prolog implementation.   
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Rename a rule.                                                            
+-- ------------------------------------------------------------------------- 
+
+renamerule :: forall fof atom term v f. (FirstOrderFormula fof atom v, {-Formula fof term v,-} Atom atom term v, Term term v f, Ord fof) =>
+              Int -> (Set.Set fof, fof) -> ((Set.Set fof, fof), Int)
+renamerule k (asm,c) =
+    ((Set.map inst asm, inst c), k + Set.size fvs)
+    where
+      fvs = fv (list_conj (Set.insert c asm)) :: Set.Set v
+      vvs = Map.fromList (map (\ (v, i) -> (v, vt (fromString ("_" ++ show i)))) (zip (Set.toList fvs) [k..])) :: Map.Map v term
+      inst = subst vvs :: fof -> fof
+
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Basic prover for Horn clauses based on backchaining with unification.     *)
+(* ------------------------------------------------------------------------- *)
+
+let rec backchain rules n k env goals =
+  match goals with
+    [] -> env
+  | g::gs ->
+     if n = 0 then failwith "Too deep" else
+     tryfind (fun rule ->
+        let (a,c),k' = renamerule k rule in
+        backchain rules (n - 1) k' (unify_literals env (c,g)) (a @ gs))
+     rules;;
+
+let hornify cls =
+  let pos,neg = partition positive cls in
+  if length pos > 1 then failwith "non-Horn clause"
+  else (map negate neg,if pos = [] then False else hd pos);;
+
+let hornprove fm =
+  let rules = map hornify (simpcnf(skolemize(Not(generalize fm)))) in
+  deepen (fun n -> backchain rules n 0 undefined [False],n) 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* A Horn example.                                                           *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let p32 = hornprove
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x))
+   ==> (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* A non-Horn example.                                                       *)
+(* ------------------------------------------------------------------------- *)
+
+(****************
+
+hornprove <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+**********)
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* Parsing rules in a Prolog-like syntax.                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let parserule s =
+  let c,rest =
+    parse_formula (parse_infix_atom,parse_atom) [] (lex(explode s)) in
+  let asm,rest1 =
+    if rest <> [] & hd rest = ":-"
+    then parse_list ","
+          (parse_formula (parse_infix_atom,parse_atom) []) (tl rest)
+    else [],rest in
+  if rest1 = [] then (asm,c) else failwith "Extra material after rule";;
+
+(* ------------------------------------------------------------------------- *)
+(* Prolog interpreter: just use depth-first search not iterative deepening.  *)
+(* ------------------------------------------------------------------------- *)
+
+let simpleprolog rules gl =
+  backchain (map parserule rules) (-1) 0 undefined [parse gl];;
+
+(* ------------------------------------------------------------------------- *)
+(* Ordering example.                                                         *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let lerules = ["0 <= X"; "S(X) <= S(Y) :- X <= Y"];;
+
+simpleprolog lerules "S(S(0)) <= S(S(S(0)))";;
+
+(*** simpleprolog lerules "S(S(0)) <= S(0)";;
+ ***)
+
+let env = simpleprolog lerules "S(S(0)) <= X";;
+apply env "X";;
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* With instantiation collection to produce a more readable result.          *)
+(* ------------------------------------------------------------------------- *)
+
+let prolog rules gl =
+  let i = solve(simpleprolog rules gl) in
+  mapfilter (fun x -> Atom(R("=",[Var x; apply i x]))) (fv(parse gl));;
+
+(* ------------------------------------------------------------------------- *)
+(* Example again.                                                            *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+prolog lerules "S(S(0)) <= X";;
+
+(* ------------------------------------------------------------------------- *)
+(* Append example, showing symmetry between inputs and outputs.              *)
+(* ------------------------------------------------------------------------- *)
+
+let appendrules =
+  ["append(nil,L,L)"; "append(H::T,L,H::A) :- append(T,L,A)"];;
+
+prolog appendrules "append(1::2::nil,3::4::nil,Z)";;
+
+prolog appendrules "append(1::2::nil,Y,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,3::4::nil,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,Y,1::2::3::4::nil)";;
+
+(* ------------------------------------------------------------------------- *)
+(* However this way round doesn't work.                                      *)
+(* ------------------------------------------------------------------------- *)
+
+(***
+ *** prolog appendrules "append(X,3::4::nil,X)";;
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* A sorting example (from Lloyd's "Foundations of Logic Programming").      *)
+(* ------------------------------------------------------------------------- *)
+
+let sortrules =
+ ["sort(X,Y) :- perm(X,Y),sorted(Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+prolog sortrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+(* ------------------------------------------------------------------------- *)
+(* Yet with a simple swap of the first two predicates...                     *)
+(* ------------------------------------------------------------------------- *)
+
+let badrules =
+ ["sort(X,Y) :- sorted(Y), perm(X,Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+(*** This no longer works
+
+prolog badrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+ ***)
+END_INTERACTIVE;;                           
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/Prop.hs b/testdata/split-expected/Data/Logic/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Prop.hs
@@ -0,0 +1,451 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Prop
+    ( eval
+    , atoms
+    , onAllValuations
+    , TruthTable
+    , TruthTableRow
+    , truthTable
+    , tautology
+    , unsatisfiable
+    , satisfiable
+    , rawdnf
+    , purednf
+    , dnf
+    , dnf'
+    , trivial
+    , psimplify
+    , nnf
+    , simpdnf
+    , simpcnf
+    , positive
+    , negative
+    , negate
+    , distrib
+    , list_disj
+    , list_conj
+    -- previously unexported
+    , pSubst
+    , dual
+    , nenf
+    , mkLits
+    , allSatValuations
+    , dnf0
+    , cnf
+    , cnf'
+    ) where
+
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, ifElse, true)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Literal.ToPropositional (toPropositional)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Formulas.Propositional (atom_union, on_atoms)
+import Data.Logic.Harrison.Lib (distrib', fpf, setAny)
+import qualified Data.Map as Map (empty, findWithDefault, insert, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, intersection, isProperSubsetOf, map, minView, null, partition, Set, singleton, toAscList, union)
+import Prelude hiding (negate)
+
+-- type Map a = Map.Map a Bool
+-- m0 = Map.empty
+-- ins :: forall a. Ord a => a -> Bool -> Map a -> Map a
+-- ins = Map.insert
+-- m ! k = Map.findWithDefault False k m
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of propositional formulas.                                        
+-- ------------------------------------------------------------------------- 
+
+{-
+let parse_propvar vs inp =
+  match inp with
+    p::oinp when p /= "(" -> Atom(P(p)),oinp
+  | _ -> failwith "parse_propvar";;
+
+let parse_prop_formula = make_parser
+  (parse_formula ((fun _ _ -> failwith ""),parse_propvar) []);;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Set this up as default for quotations.                                    
+-- ------------------------------------------------------------------------- 
+
+{-
+let default_parser = parse_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printer.                                                                  
+-- ------------------------------------------------------------------------- 
+
+{-
+let print_propvar prec p = print_string(pname p);;
+
+let print_prop_formula = print_qformula print_propvar;;
+
+#install_printer print_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Map.Map atomic Bool -> Bool
+eval fm v =
+    foldPropositional co id at fm
+    where
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at x = Map.findWithDefault False x v
+
+{-
+START_INTERACTIVE;;
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> false | P"r" -> true);;
+
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> true | P"r" -> false);;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Return the set of propositional variables in a formula.                   
+-- ------------------------------------------------------------------------- 
+
+atoms :: Ord atomic => PropositionalFormula formula atomic => formula -> Set.Set atomic
+atoms = atom_union Set.singleton
+
+-- ------------------------------------------------------------------------- 
+-- Code to print out truth tables.                                           
+-- ------------------------------------------------------------------------- 
+
+onAllValuations :: (Ord a) =>
+                   (r -> r -> r)         -- ^ Combine function for result type
+                -> (Map.Map a Bool -> r) -- ^ The substitution function
+                -> Map.Map a Bool        -- ^ The default valuation function for atoms not in ps
+                -> Set.Set a             -- ^ The variables to vary
+                -> r
+onAllValuations _ subfn v ps | Set.null ps = subfn v
+onAllValuations append subfn v ps =
+    case Set.minView ps of
+      Nothing -> error "onAllValuations"
+      Just (p, ps') ->
+          append -- Do the valuations of the remaining variables with  set to false
+                 (onAllValuations append subfn (Map.insert p False v) ps')
+                 -- Do the valuations of the remaining variables with  set to true
+                 (onAllValuations append subfn (Map.insert p True v) ps')
+
+type TruthTableRow = ([Bool], Bool)
+type TruthTable a = ([a], [TruthTableRow])
+
+truthTable :: forall formula atom. (PropositionalFormula formula atom, Eq atom, Ord atom) =>
+              formula -> TruthTable atom
+truthTable fm =
+    (atl, onAllValuations (++) mkRow Map.empty ats)
+    where
+      mkRow :: Map.Map atom Bool      -- ^ The current variable assignment
+            -> [TruthTableRow]          -- ^ The variable assignments and the formula value
+      mkRow v = [(map (\ k -> Map.findWithDefault False k v) atl, eval fm v)]
+      atl = Set.toAscList ats
+      ats = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- Recognizing tautologies.                                                  
+-- ------------------------------------------------------------------------- 
+
+tautology :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+tautology fm = onAllValuations (&&) (eval fm) Map.empty (atoms fm)
+
+-- ------------------------------------------------------------------------- 
+-- Related concepts.                                                         
+-- ------------------------------------------------------------------------- 
+
+
+unsatisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+unsatisfiable fm = tautology ((.~.) fm)
+
+satisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+satisfiable = not . unsatisfiable
+
+-- ------------------------------------------------------------------------- 
+-- Substitution operation.                                                   
+-- ------------------------------------------------------------------------- 
+
+-- pSubst :: Ord a => Map.Map a (Formula a) -> Formula a -> Formula a
+pSubst :: (PropositionalFormula formula atomic, Ord atomic) => Map.Map atomic formula -> formula -> formula
+pSubst subfn fm = on_atoms (\ p -> maybe (atomic p) id (fpf subfn p)) fm
+
+-- ------------------------------------------------------------------------- 
+-- Dualization.                                                              
+-- ------------------------------------------------------------------------- 
+
+dual :: forall formula atomic. (PropositionalFormula formula atomic) => formula -> formula
+dual fm =
+    foldPropositional co (fromBool . not) at fm
+    where
+      co ((:~:) _) = fm
+      co (BinOp p (:&:) q) = dual p .|. dual q
+      co (BinOp p (:|:) q) = dual p .&. dual q
+      co _ = error "dual: Formula involves connectives ==> or <=>";;
+      at = atomic
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+psimplify1 :: (PropositionalFormula r a, Eq r) => r -> r
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) fm') = foldPropositional simplifyNotCombine (fromBool . not) (\ _ -> fm) fm'
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _         ) -> r
+            (Just False, (:&:), _         ) -> false
+            (_,          (:&:), Just True ) -> l
+            (_,          (:&:), Just False) -> false
+            (Just True,  (:|:), _         ) -> true
+            (Just False, (:|:), _         ) -> r
+            (_,          (:|:), Just True ) -> true
+            (_,          (:|:), Just False) -> l
+            (Just True,  (:=>:), _         ) -> r
+            (Just False, (:=>:), _         ) -> true
+            (_,          (:=>:), Just True ) -> true
+            (_,          (:=>:), Just False) -> (.~.) l
+            (Just True,  (:<=>:), _         ) -> r
+            (Just False, (:<=>:), _         ) -> (.~.) r
+            (_,          (:<=>:), Just True ) -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _ -> fm
+
+      simplifyNotCombine ((:~:) p) = p
+      simplifyNotCombine _ = fm
+
+psimplify :: forall formula atomic. (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+psimplify fm =
+    foldPropositional c (\ _ -> fm) (\ _ -> fm) fm
+    where
+      c :: Combination formula -> formula
+      c ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      c (BinOp p op q) = psimplify1 (binop (psimplify p) op (psimplify q))
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.                                              
+-- ------------------------------------------------------------------------- 
+
+negative :: forall lit atom. Literal lit atom => lit -> Bool
+negative lit =
+    foldLiteral neg tf a lit
+    where
+      neg _ = True
+      tf = not
+      a _ = False
+
+positive :: Literal lit atom => lit -> Bool
+positive = not . negative
+
+negate :: PropositionalFormula formula atomic => formula -> formula
+negate lit =
+    foldPropositional c (fromBool . not) a lit
+    where
+      c ((:~:) p) = p
+      c _ = (.~.) lit
+      a _ = (.~.) lit
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf' :: PropositionalFormula formula atomic => formula -> formula
+nnf' fm =
+    foldPropositional nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfCombine ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf' ((.~.) p) .|. (nnf' q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf' p .&. nnf' q) .|. (nnf' ((.~.) p) .&. nnf' ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf' p .&. nnf' q
+      nnfCombine (BinOp p (:|:) q) = nnf' p .|. nnf' q
+      nnfNotCombine ((:~:) p) = nnf' p
+      nnfNotCombine (BinOp p (:&:) q) = nnf' ((.~.) p) .|. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf' ((.~.) p) .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf' p .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' ((.~.) q)) .|. nnf' ((.~.) p) .&. nnf' q
+
+-- ------------------------------------------------------------------------- 
+-- Roll in simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+nnf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nnf = nnf' . psimplify
+
+-- ------------------------------------------------------------------------- 
+-- Simple negation-pushing when we don't care to distinguish occurrences.    
+-- ------------------------------------------------------------------------- 
+
+nenf' :: PropositionalFormula formula atomic => formula -> formula
+nenf' fm =
+    foldPropositional nenfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nenfCombine ((:~:) p) = foldPropositional nenfNotCombine (\ _ -> fm) (\ _ -> fm) p
+      nenfCombine (BinOp p (:&:) q) = nenf' p .&. nenf' q
+      nenfCombine (BinOp p (:|:) q) = nenf' p .|. nenf' q
+      nenfCombine (BinOp p (:=>:) q) = nenf' ((.~.) p) .|. nenf' q
+      nenfCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' q
+      nenfNotCombine ((:~:) p) = p
+      nenfNotCombine (BinOp p (:&:) q) = nenf' ((.~.) p) .|. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:|:) q) = nenf' ((.~.) p) .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:=>:) q) = nenf' p .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' ((.~.) q) -- really?  how is this asymmetrical?
+
+nenf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nenf = nenf' . psimplify
+
+{-
+# Not (prime 2) ->
+  <<~(~(((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1))>>
+
+# nenf (Not (prime 2)) -> 
+  <<((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1>>
+
+> pretty ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N)))
+     (out0 ⇔ x0 ∧ y0) ∧ ¬out1 ∧ out1 ∧ ¬out0
+
+> pretty (nenf ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N))))
+     (out0 ⇔ x0 ∨ y0) ∨ ¬out1 ∨ out1 ∨ ¬out0
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Disjunctive normal form (DNF) via truth tables.                           
+-- ------------------------------------------------------------------------- 
+
+list_conj :: (PropositionalFormula formula atomic, Ord formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: PropositionalFormula formula atomic => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (PropositionalFormula formula atomic, Ord formula, Ord atomic) =>
+          Set.Set formula -> Map.Map atomic Bool -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+allSatValuations :: Ord a => (Map.Map a Bool -> Bool) -> Map.Map a Bool -> Set.Set a -> [Map.Map a Bool]
+allSatValuations subfn v pvs =
+    case Set.minView pvs of
+      Nothing -> if subfn v then [v] else []
+      Just (p, ps) -> (allSatValuations subfn (Map.insert p False v) ps) ++
+                      (allSatValuations subfn (Map.insert p True v) ps)
+
+dnf0 :: forall formula atomic. (PropositionalFormula formula atomic, Ord atomic, Ord formula) => formula -> formula
+dnf0 fm =
+    list_disj (Set.fromList (map (mkLits (Set.map atomic pvs)) satvals))
+    where
+      satvals = allSatValuations (eval fm) Map.empty pvs
+      pvs = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- DNF via distribution.                                                     
+-- ------------------------------------------------------------------------- 
+
+distrib :: PropositionalFormula formula atomic => formula -> formula
+distrib fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) s) =
+          foldPropositional c' tf a s
+          where c' (BinOp q (:|:) r) = distrib (p .&. q) .|. distrib (p .&. r)
+                c' _ =
+                    foldPropositional c'' tf a p
+                    where c'' (BinOp q (:|:) r) = distrib (q .&. s) .|. distrib (r .&. s)
+                          c'' _ = fm
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+rawdnf :: PropositionalFormula formula atomic => formula -> formula
+rawdnf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib (rawdnf p .&. rawdnf q)
+      c (BinOp p (:|:) q) = rawdnf p .|. rawdnf q
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.                                    
+-- ------------------------------------------------------------------------- 
+
+purednf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purednf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c ((:~:) p) = Set.map (Set.map (.~.)) (purednf p)
+      c _ = error "purednf" -- Set.singleton (Set.singleton fm)
+      tf x = Set.singleton (Set.singleton (fromBool x))
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Literal lit atom, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (pos, neg) = Set.partition positive lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpdnf fm =
+    foldPropositional c tf a fm
+    where
+      c :: Combination pf -> Set.Set (Set.Set lit)
+      c _ = Set.filter (\ d -> not (setAny (\ d' -> Set.isProperSubsetOf d' d) djs)) djs
+          where djs = Set.filter (not . trivial) (purednf (nnf fm))
+      tf = ifElse (Set.singleton Set.empty) Set.empty
+      a :: atom -> Set.Set (Set.Set lit)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Mapping back to a formula.                                                
+-- ------------------------------------------------------------------------- 
+
+dnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+dnf = list_disj . Set.map (list_conj . Set.map (toPropositional id))
+
+dnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+dnf' = dnf . (simpdnf :: pf -> Set.Set (Set.Set pf))
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+purecnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+simpcnf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpcnf fm =
+    foldPropositional c tf a fm
+    where
+      tf = ifElse Set.empty (Set.singleton Set.empty)
+      -- Discard any clause that is the proper subset of another clause
+      c _ = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+cnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+cnf = list_conj . Set.map (list_disj . Set.map (toPropositional id))
+
+cnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+cnf' = cnf . (simpcnf :: pf -> Set.Set (Set.Set pf))
diff --git a/testdata/split-expected/Data/Logic/Harrison/PropExamples.hs b/testdata/split-expected/Data/Logic/Harrison/PropExamples.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/PropExamples.hs
@@ -0,0 +1,392 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+module Data.Logic.Harrison.PropExamples
+    ( Atom(..)
+    , N
+    , prime
+    , ramsey
+    , tests
+    ) where
+
+import Data.Bits (Bits, shiftR)
+import Data.Logic.Classes.Combine ((.<=>.), (.=>.), (.&.), (.|.), Combinable, Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (true, false)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), botFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Lib (allsets)
+import Data.Logic.Harrison.Prop (tautology, list_conj, list_disj, psimplify)
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Set as Set
+import Prelude hiding (sum)
+import Test.HUnit
+import Text.PrettyPrint (text)
+
+tests :: Test
+tests = TestList [test01, test02, test03]
+
+-- ========================================================================= 
+-- Some propositional formulas to test, and functions to generate classes.   
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Generate assertion equivalent to R(s,t) <= n for the Ramsey number R(s,t) 
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P String a (Maybe a) deriving (Eq, Ord, Show)
+
+instance Pretty (Atom N) where
+    pretty (P s n mm) = text (s ++ show n ++ maybe "" (\ m -> "." ++ show m) mm)
+
+instance HasFixity (Atom N) where
+    fixity = const botFixity
+
+type N = Int
+
+ramsey :: forall formula.
+          (PropositionalFormula formula (Atom N), Ord formula) =>
+          Int -> Int -> N -> formula
+ramsey s t n =
+  let vertices = Set.fromList [1 .. n] in
+  let yesgrps = Set.map (allsets (2 :: Int)) (allsets s vertices)
+      nogrps = Set.map (allsets (2 :: Int)) (allsets t vertices) in
+  let e xs = let [m, n] = Set.toAscList xs in C.atomic (P "p" m (Just n)) in
+  list_disj (Set.map (list_conj . Set.map e) yesgrps) .|. list_disj (Set.map (list_conj . Set.map (\ p -> (.~.)(e p))) nogrps)
+
+-- ------------------------------------------------------------------------- 
+-- Some currently tractable examples.                                        
+-- ------------------------------------------------------------------------- 
+
+test01 :: Test
+test01 = TestList [{- TestCase (assertEqual "ramsey 3 3 4"
+                                             (Combine
+                                              (BinOp
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp
+                                                   (Atom (P "p" 1 (Just 4)))
+                                                   (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 2 (Just 4)))
+                                                     (:&:)
+                                                     (Atom (P "p" 1 (Just 2)))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 1 (Just 4)))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 3 (Just 4)))
+                                                       (:&:)
+                                                       (Atom (P "p" 1 (Just 3)))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 2 (Just 4)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 3 (Just 4)))
+                                                         (:&:)
+                                                         (Atom (P "p" 2 (Just 3)))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 1 (Just 3)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 2 (Just 3)))
+                                                         (:&:)
+                                                         (Atom (P "p" 1 (Just 2)))))))))))))
+                                               (:|:)
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 4))))) (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 1 (Just 2)))))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp (Combine
+                                                            ((:~:) (Atom (P "p" 1 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 2)))))))))))))))))
+                                         (ramsey 3 3 4 :: Formula (Atom N))), -}
+                   TestCase (assertEqual "tautology (ramsey 3 3 5)" False (tautology (ramsey 3 3 5 :: Formula (Atom N)))),
+                   TestCase (assertEqual "tautology (ramsey 3 3 6)" True (tautology (ramsey 3 3 6 :: Formula (Atom N))))]
+
+-- ------------------------------------------------------------------------- 
+-- Half adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+halfsum :: forall formula. Combinable formula => formula -> formula -> formula
+halfsum x y = x .<=>. ((.~.) y)
+
+halfcarry :: forall formula. Combinable formula => formula -> formula -> formula
+halfcarry x y = x .&. y
+
+ha :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula
+ha x y s c = (s .<=>. halfsum x y) .&. (c .<=>. halfcarry x y)
+
+-- ------------------------------------------------------------------------- 
+-- Full adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+carry :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+carry x y z = (x .&. y) .|. ((x .|. y) .&. z)
+
+sum :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+sum x y z = halfsum (halfsum x y) z
+
+fa :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula -> formula
+fa x y z s c = (s .<=>. sum x y z) .&. (c .<=>. carry x y z)
+
+-- ------------------------------------------------------------------------- 
+-- Useful idiom.                                                             
+-- ------------------------------------------------------------------------- 
+
+conjoin :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a) => (a -> formula) -> Set.Set a -> formula
+conjoin f l = list_conj (Set.map f l)
+
+-- ------------------------------------------------------------------------- 
+-- n-bit ripple carry adder with carry c(0) propagated in and c(n) out.      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> formula
+ripplecarry x y c out n =
+    conjoin (\ i -> fa (x i) (y i) (c i) (out i) (c(i + 1))) (Set.fromList [0 .. (n - 1)])
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+mk_index :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> formula
+mk_index x i = C.atomic (P x i Nothing)
+mk_index2 :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> a -> formula
+mk_index2 x i j = C.atomic (P x i (Just j))
+
+test02 = TestCase (assertEqual "ripplecarry x y c out 2"
+                               (Combine (BinOp (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 1 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 1 Nothing)))))))) (:&:)
+                                                         (Combine (BinOp (Atom (P "C" 2 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:&:) (Atom (P "Y" 1 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:|:) (Atom (P "Y" 1 Nothing)))) (:&:) (Atom (P "C" 1 Nothing)))))))))) (:&:)
+                                         (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 0 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 0 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 0 Nothing)))))))) (:&:)
+                                                   (Combine (BinOp (Atom (P "C" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:&:) (Atom (P "Y" 0 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:|:) (Atom (P "Y" 0 Nothing)))) (:&:) (Atom (P "C" 0 Nothing))))))))))))
+                               {- <<((OUT_0 <=> (X_0 <=> ~Y_0) <=> ~C_0) /\
+                                      (C_1 <=> X_0 /\ Y_0 \/ (X_0 \/ Y_0) /\ C_0)) /\
+                                     (OUT_1 <=> (X_1 <=> ~Y_1) <=> ~C_1) /\
+                                     (C_2 <=> X_1 /\ Y_1 \/ (X_1 \/ Y_1) /\ C_1)>> -}
+                               (let [x, y, out, c] = map mk_index ["X", "Y", "OUT", "C"] in
+                                ripplecarry x y c out 2 :: Formula (Atom N)))
+
+-- ------------------------------------------------------------------------- 
+-- Special case with 0 instead of c(0).                                      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry0 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry0 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then false else c i) out n)
+
+-- ------------------------------------------------------------------------- 
+-- Carry-select adder                                                        
+-- ------------------------------------------------------------------------- 
+
+ripplecarry1 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry1 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then true else c i) out n)
+
+mux :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+mux sel in0 in1 = (((.~.) sel) .&. in0) .|. (sel .&. in1)
+
+offset :: forall t a. Num a => a -> (a -> t) -> a -> t
+offset n x i = x (n + i)
+
+carryselect :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> a -> formula
+carryselect x y c0 c1 s0 s1 c s n k =
+  let k' = min n k in
+  let fm = ((ripplecarry0 x y c0 s0 k') .&. (ripplecarry1 x y c1 s1 k')) .&.
+           (((c k') .<=>. (mux (c 0) (c0 k') (c1 k'))) .&.
+            (conjoin (\ i -> (s i) .<=>. (mux (c 0) (s0 i) (s1 i)))
+                             (Set.fromList [0 .. (k' - 1)]))) in
+  if k' < k then fm else
+  fm .&. (carryselect
+          (offset k x) (offset k y) (offset k c0) (offset k c1)
+          (offset k s0) (offset k s1) (offset k c) (offset k s)
+          (n - k) k)
+
+-- ------------------------------------------------------------------------- 
+-- Equivalence problems for carry-select vs ripple carry adders.             
+-- ------------------------------------------------------------------------- 
+
+mk_adder_test :: forall formula a. (PropositionalFormula formula (Atom a), Ord a, Ord formula, Num a, Enum a) =>
+                 a -> a -> formula
+mk_adder_test n k =
+  let [x, y, c, s, c0, s0, c1, s1, c2, s2] =
+          map mk_index ["x", "y", "c", "s", "c0", "s0", "c1", "s1", "c2", "s2"] in
+  (((carryselect x y c0 c1 s0 s1 c s n k) .&.
+    ((.~.) (c 0))) .&.
+   (ripplecarry0 x y c2 s2 n)) .=>.
+  (((c n) .<=>. (c2 n)) .&.
+   (conjoin (\ i -> (s i) .<=>. (s2 i)) (Set.fromList [0 .. (n - 1)])))
+
+-- ------------------------------------------------------------------------- 
+-- Ripple carry stage that separates off the final result.                   
+--                                                                           
+--       UUUUUUUUUUUUUUUUUUUU  (u)                                           
+--    +  VVVVVVVVVVVVVVVVVVVV  (v)                                           
+--                                                                           
+--    = WWWWWWWWWWWWWWWWWWWW   (w)                                           
+--    +                     Z  (z)                                           
+-- ------------------------------------------------------------------------- 
+
+rippleshift :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> formula
+            -> (a -> formula)
+            -> a -> formula
+rippleshift u v c z w n =
+  ripplecarry0 u v (\ i -> if i == n then w(n - 1) else c(i + 1))
+                   (\ i -> if i == 0 then z else w(i - 1)) n
+-- ------------------------------------------------------------------------- 
+-- Naive multiplier based on repeated ripple carry.                          
+-- ------------------------------------------------------------------------- 
+
+multiplier :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+              (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> formula)
+           -> a
+           -> formula
+multiplier x u v out n =
+  if n == 1 then ((out 0) .<=>. (x 0 0)) .&. ((.~.)(out 1)) else
+  psimplify (((out 0) .<=>. (x 0 0)) .&.
+             ((rippleshift
+               (\ i -> if i == n - 1 then false else x 0 (i + 1))
+               (x 1) (v 2) (out 1) (u 2) n) .&.
+              (if n == 2 then ((out 2) .<=>. (u 2 0)) .&. ((out 3) .<=>. (u 2 1)) else
+                   conjoin (\ k -> rippleshift (u k) (x k) (v(k + 1)) (out k)
+                                   (if k == n - 1 then \ i -> out(n + i)
+                                    else u(k + 1)) n) (Set.fromList [2 .. (n - 1)]))))
+
+-- ------------------------------------------------------------------------- 
+-- Primality examples.                                                       
+-- For large examples, should use "num" instead of "int" in these functions. 
+-- ------------------------------------------------------------------------- 
+
+bitlength :: forall b a. (Num a, Num b, Bits b) => b -> a
+bitlength x = if x == 0 then 0 else 1 + bitlength (shiftR x 1);;
+
+bit :: forall a b. (Num a, Eq a, Bits b, Integral b) => a -> b -> Bool
+bit n x = if n == 0 then x `mod` 2 == 1 else bit (n - 1) (shiftR x 1)
+
+congruent_to :: forall formula atomic a b. (Bits b, PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Integral b, Enum a) =>
+                (a -> formula) -> b -> a -> formula
+congruent_to x m n =
+  conjoin (\ i -> if bit i m then x i else (.~.)(x i))
+          (Set.fromList [0 .. (n - 1)])
+
+prime :: forall formula. (PropositionalFormula formula (Atom N), Ord formula) => N -> formula
+prime p =
+  let [x, y, out] = map mk_index ["x", "y", "out"] in
+  let m i j = (x i) .&. (y j)
+      [u, v] = map mk_index2 ["u", "v"] in
+  let (n :: Int) = bitlength p in
+  (.~.) (multiplier m u v out (n - 1) .&. congruent_to out p (max n (2 * n - 2)))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+type F = Formula (Atom Int)
+
+deriving instance Show F
+
+{-
+instance Constants F where
+    fromBool True = 
+-}
+
+test03 :: Test
+test03 =
+    TestList [TestCase (assertEqual "tautology(prime 7)" True (tautology(prime 7 :: F))),
+              TestCase (assertEqual "tautology(prime 9)" False (tautology(prime 9 :: F))),
+              TestCase (assertEqual "tautology(prime 11)" True (tautology(prime 11 :: F)))]
diff --git a/testdata/split-expected/Data/Logic/Harrison/Resolution.hs b/testdata/split-expected/Data/Logic/Harrison/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Resolution.hs
@@ -0,0 +1,1044 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Resolution
+    ( resolution1
+    , resolution2
+    , resolution3
+    , presolution
+    , matchAtomsEq
+    ) where
+
+import Data.Logic.Classes.Atom (Atom(match))
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), zipFirstOrder)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(vt, foldTerm))
+import Data.Logic.Classes.Variable (Variable(prefix))
+import Data.Logic.Failing (failing, Failing(..))
+import Data.Logic.Harrison.FOL (fv, generalize, list_conj, list_disj, subst)
+import Data.Logic.Harrison.Lib (allnonemptysubsets, allpairs, allsubsets, apply, defined, setAll, setAny, settryfind, (|->))
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf, trivial)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (unify_literals)
+import Data.Logic.Harrison.Unif (solve)
+import qualified Data.Map as Map (empty, fromList, Map)
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set (difference, empty, filter, fold, insert, map, member, minView, null, partition, Set, singleton, toList, union)
+
+-- ========================================================================= 
+-- Resolution.                                                               
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- MGU of a set of literals.                                                 
+-- ------------------------------------------------------------------------- 
+
+mgu :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+       Set.Set lit -> Map.Map v term -> Failing (Map.Map v term)
+mgu l env =
+    case Set.minView l of
+      Just (a, rest) ->
+          case Set.minView rest of
+            Just (b, _) -> unify_literals env a b >>= mgu rest
+            _ -> Success (solve env)
+      _ -> Success (solve env)
+
+unifiable :: (Literal lit atom, Term term v f, Atom atom term v) =>
+             lit -> lit -> Bool
+unifiable p q = failing (const False) (const True) (unify_literals Map.empty p q)
+
+-- ------------------------------------------------------------------------- 
+-- Rename a clause.                                                          
+-- ------------------------------------------------------------------------- 
+
+rename :: (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+          (v -> v) -> Set.Set fof -> Set.Set fof
+rename pfx cls =
+    Set.map (subst (Map.fromList (zip fvs vvs))) cls
+    where
+      -- fvs :: [v]
+      fvs = Set.toList (fv (list_disj cls))
+      -- vvs :: [term]
+      vvs = map (vt . pfx) fvs
+
+-- ------------------------------------------------------------------------- 
+-- General resolution rule, incorporating factoring as in Robinson's paper.  
+-- ------------------------------------------------------------------------- 
+
+resolvents :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+              Set.Set fof -> Set.Set fof -> fof -> Set.Set fof -> Set.Set fof
+resolvents cl1 cl2 p acc =
+    if Set.null ps2 then acc else Set.fold doPair acc pairs
+    where
+      doPair (s1,s2) sof =
+          case mgu (Set.union s1 (Set.map (.~.) s2)) Map.empty of
+            Success mp -> Set.union (Set.map (subst mp) (Set.union (Set.difference cl1 s1) (Set.difference cl2 s2))) sof
+            Failure _ -> sof
+      -- pairs :: Set.Set (Set.Set fof, Set.Set fof)
+      pairs = allpairs (,) (Set.map (Set.insert p) (allsubsets ps1)) (allnonemptysubsets ps2)
+      -- ps1 :: Set.Set fof
+      ps1 = Set.filter (\ q -> q /= p && unifiable p q) cl1
+      -- ps2 :: Set.Set fof
+      ps2 = Set.filter (unifiable ((.~.) p)) cl2
+
+resolve_clauses :: forall fof atom v term f.
+                   (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                   Set.Set fof -> Set.Set fof -> Set.Set fof
+resolve_clauses cls1 cls2 =
+    let cls1' = rename (prefix "x") cls1
+        cls2' = rename (prefix "y") cls2 in
+    Set.fold (resolvents cls1' cls2') Set.empty cls1'
+
+-- ------------------------------------------------------------------------- 
+-- Basic "Argonne" loop.                                                     
+-- ------------------------------------------------------------------------- 
+
+resloop1 :: forall atom v term f fof. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+resloop1 used unused =
+    maybe (Failure ["No proof found"]) step (Set.minView unused)
+    where
+      step (cl, ros) =
+          if Set.member Set.empty news then return True else resloop1 used' (Set.union ros news)
+          where
+            used' = Set.insert cl used
+            -- resolve_clauses is not in the Failing monad, so setmapfilter isn't appropriate.
+            news = Set.fold Set.insert Set.empty ({-setmapfilter-} Set.map (resolve_clauses cl) used')
+
+pure_resolution1 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution1 fm = resloop1 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution1 :: forall m fof term f atom v.
+               (Literal fof atom,
+                FirstOrderFormula fof atom v,
+                PropositionalFormula fof atom,
+                Term term v f,
+                Atom atom term v,
+                Ord fof,
+                Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution1 fm = askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution1 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Matching of terms and literals.                                           
+-- ------------------------------------------------------------------------- 
+
+term_match :: forall term v f. (Term term v f) => Map.Map v term -> [(term, term)] -> Failing (Map.Map v term)
+term_match env [] = Success env
+term_match env ((p, q) : oth) =
+    foldTerm v fn p
+    where
+      v x = if not (defined env x)
+            then term_match ((x |-> q) env) oth
+            else if apply env x == Just q
+                 then term_match env oth
+                 else Failure ["term_match"]
+      fn f fa =
+          foldTerm v' fn' q
+          where
+            fn' g ga | f == g && length fa == length ga = term_match env (zip fa ga ++ oth)
+            fn' _ _ = Failure ["term_match"]
+            v' _ = Failure ["term_match"]
+{-
+  case eqs of
+    [] -> Success env
+    (Fn f fa, Fn g ga) : oth
+        | f == g && length fa == length ga ->
+           term_match env (zip fa ga ++ oth)
+    (Var x, t) : oth ->
+        if not (defined env x) then term_match ((x |-> t) env) oth
+        else if apply env x == t then term_match env oth
+        else Failure ["term_match"]
+    _ -> Failure ["term_match"]
+-}
+
+match_literals :: forall term f v fof atom. (FirstOrderFormula fof atom v, Atom atom term v, Term term v f) =>
+                  Map.Map v term -> fof -> fof -> Failing (Map.Map v term)
+match_literals env t1 t2 =
+    fromMaybe err (zipFirstOrder qu co tf at t1 t2)
+    where
+      qu _ _ _ _ _ _ = Nothing
+      co ((:~:) p) ((:~:) q) = Just $ match_literals env p q
+      co _ _ = Nothing
+      tf a b = if a == b then Just (Success env) else Nothing
+      at a1 a2 = Just (match env a1 a2)
+      err = Failure ["match_literals"]
+
+-- Identical to unifyAtomsEq except calls term_match instead of unify.
+matchAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+matchAtomsEq env a1 a2 =
+    fromMaybe err (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p ts1 q ts2 =
+          if p == q && length ts1 == length ts2
+          then Just (term_match env (zip ts1 ts2))
+          else Nothing
+      tf p q = if p == q then Just (Success env) else Nothing
+      eq pl pr ql qr = Just (term_match env [(pl, ql), (pr, qr)])
+      err = Failure ["matchAtomsEq"]
+
+{-
+    case tmp of
+      (Atom (R p a1), Atom(R q a2)) -> term_match env [(Fn p a1, Fn q a2)]
+      (Not (Atom (R p a1)), Not (Atom (R q a2))) -> term_match env [(Fn p a1, Fn q a2)]
+      _ -> Failure ["match_literals"]
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Test for subsumption                                                      
+-- ------------------------------------------------------------------------- 
+
+subsumes_clause :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v) =>
+                   Set.Set fof -> Set.Set fof -> Bool
+subsumes_clause cls1 cls2 =
+    failing (const False) (const True) (subsume Map.empty cls1)
+    where
+      -- subsume :: Map.Map v term -> Set.Set fof -> Failing (Map.Map v term)
+      subsume env cls =
+          case Set.minView cls of
+            Nothing -> Success env
+            Just (l1, clt) -> settryfind (\ l2 -> case (match_literals env l1 l2) of
+                                                    Success env' -> subsume env' clt
+                                                    Failure msgs -> Failure msgs) cls2
+-- ------------------------------------------------------------------------- 
+-- With deletion of tautologies and bi-subsumption with "unused".            
+-- ------------------------------------------------------------------------- 
+
+replace :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set fof
+        -> Set.Set (Set.Set fof)
+        -> Set.Set (Set.Set fof)
+replace cl st =
+    case Set.minView st of
+      Nothing -> Set.singleton cl
+      Just (c, st') -> if subsumes_clause cl c
+                       then Set.insert cl st'
+                       else Set.insert c (replace cl st')
+
+incorporate :: forall fof term f v atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+               Set.Set fof
+            -> Set.Set fof
+            -> Set.Set (Set.Set fof)
+            -> Set.Set (Set.Set fof)
+incorporate gcl cl unused =
+    if trivial cl || setAny (\ c -> subsumes_clause c cl) (Set.insert gcl unused)
+    then unused
+    else replace cl unused
+
+resloop2 :: forall fof term f v atom. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof)
+         -> Set.Set (Set.Set fof)
+         -> Failing Bool
+resloop2 used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl {- :: Set.Set fof-}, ros {- :: Set.Set (Set.Set fof) -}) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = {-Set.fold Set.union Set.empty-} (Set.map (resolve_clauses cl) used') in
+          if Set.member Set.empty news then return True else resloop2 used' (Set.fold (incorporate cl) ros news)
+
+pure_resolution2 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution2 fm = resloop2 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution2 :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution2 fm = askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_resolution2 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Positive (P1) resolution.                                                 
+-- ------------------------------------------------------------------------- 
+
+presolve_clauses :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    Set.Set fof -> Set.Set fof -> Set.Set fof
+presolve_clauses cls1 cls2 =
+    if setAll positive cls1 || setAll positive cls2
+    then resolve_clauses cls1 cls2
+    else Set.empty
+
+presloop :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+presloop used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl, ros) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = Set.map (presolve_clauses cl) used' in
+          if Set.member Set.empty news
+          then Success True
+          else presloop used' (Set.fold (incorporate cl) ros news)
+
+pure_presolution :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_presolution fm = presloop Set.empty (simpcnf (specialize (pnf fm)))
+
+presolution :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+presolution fm =
+    askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_presolution . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Introduce a set-of-support restriction.                                   
+-- ------------------------------------------------------------------------- 
+
+pure_resolution3 :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution3 fm =
+    uncurry resloop2 (Set.partition (setAny positive) (simpcnf (specialize (pnf fm))))
+
+resolution3 :: forall fof atom term v f m. (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution3 fm =
+    askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution3 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+{-
+-- ------------------------------------------------------------------------- 
+-- The Pelletier examples again.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time presolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time presolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time presolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time presolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time presolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time presolution
+ <<p \/ ~p>>;;
+
+let p7 = time presolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time presolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time presolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time presolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time presolution
+ <<p <=> p>>;;
+
+let p12 = time presolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time presolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time presolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time presolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time presolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time presolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time presolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time presolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time presolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+let p21 = time presolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time presolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time presolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time presolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time presolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time presolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time presolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time presolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time presolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time presolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\
+   (forall x. (G(x) ==> ~U(x)) ==> P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time presolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time presolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time presolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time presolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time presolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time presolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time presolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+{- ** This one seems too slow
+
+let p38 = time presolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ** -}
+
+let p39 = time presolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time presolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time presolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+{- ** Also very slow
+
+let p42 = time presolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ** -}
+
+{- ** and this one too..
+
+let p43 = time presolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ** -}
+
+let p44 = time presolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+{- ** and this...
+
+let p45 = time presolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ** -}
+
+{- ** and this
+
+let p46 = time presolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time presolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time presolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time presolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time presolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time presolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+let gilmore_1 = time presolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time presolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time presolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time presolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time presolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time presolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This one still isn't easy!
+
+let gilmore_9 = time presolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time presolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+*********** -}
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Example                                                                   
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let gilmore_1 = resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletiers yet again.                                                     
+-- ------------------------------------------------------------------------- 
+
+{- ************
+
+let p1 = time resolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time resolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time resolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time resolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time resolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time resolution
+ <<p \/ ~p>>;;
+
+let p7 = time resolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time resolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time resolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time resolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time resolution
+ <<p <=> p>>;;
+
+let p12 = time resolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time resolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time resolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time resolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time resolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time resolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* Monadic Predicate Logic.                                                  *)
+(* ------------------------------------------------------------------------- *)
+
+let p18 = time resolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time resolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time resolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time resolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P) ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time resolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time resolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time resolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time resolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time resolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time resolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time resolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time resolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time resolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time resolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time resolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time resolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time resolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+   ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+  ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time resolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(*  Full predicate logic (without Identity and Functions)                    *)
+(* ------------------------------------------------------------------------- *)
+
+let p36 = time resolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time resolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+(*** This one seems too slow
+
+let p38 = time resolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ***)
+
+let p39 = time resolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time resolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time resolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+(*** Also very slow
+
+let p42 = time resolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ***)
+
+(*** and this one too..
+
+let p43 = time resolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ***)
+
+let p44 = time resolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+(*** and this...
+
+let p45 = time resolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ***)
+
+(*** and this
+
+let p46 = time resolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Manthey and Bry, CADE-9.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let p55 = time resolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time resolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* See info-hol, circa 1500.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let p58 = time resolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time resolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time resolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* From Gilmore's classic paper.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let gilmore_1 = time resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+(*** This is not valid, according to Gilmore
+
+let gilmore_2 = time resolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ***)
+
+let gilmore_3 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time resolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time resolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time resolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time resolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+(*** This one still isn't easy!
+
+let gilmore_9 = time resolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Davis-Putnam papers where Gilmore procedure is poor.         *)
+(* ------------------------------------------------------------------------- *)
+
+let davis_putnam_example = time resolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* The (in)famous Los problem.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let los = time resolution
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+************* -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/Skolem.hs b/testdata/split-expected/Data/Logic/Harrison/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Skolem.hs
@@ -0,0 +1,355 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Skolem
+    ( simplify
+    -- , simplify'
+    , lsimplify
+    , nnf
+    -- , nnf'
+    , pnf
+    -- , pnf'
+    , functions
+    -- , functions'
+    , SkolemT
+    , Skolem
+    , runSkolem
+    , runSkolemT
+    , specialize
+    , skolemize
+    -- , literal
+    , askolemize
+    , skolemNormalForm
+    -- , prenex'
+    , skolem
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (StateT(runStateT))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(exists, for_all, foldFirstOrder), quant, Quant(..), toPropositional)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import qualified Data.Logic.Classes.Skolem as C (Skolem(toSkolem))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Classes.Variable (Variable(variant))
+import Data.Logic.Harrison.FOL (fv, subst)
+import Data.Logic.Harrison.Lib ((|=>))
+import qualified Data.Map as Map (singleton)
+import qualified Data.Set as Set (empty, member, Set, toAscList, union)
+
+-- =========================================================================
+-- Prenex and Skolem normal forms.                                           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification. Like "psimplify" but with quantifier clauses.     
+-- ------------------------------------------------------------------------- 
+
+simplify1 :: (FirstOrderFormula fof atom v,
+              -- Formula fof term v,
+              Atom atom term v,
+              Term term v f) => fof -> fof
+simplify1 fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = if Set.member x (fv p) then fm else p
+      co ((:~:) p) = foldFirstOrder (\ _ _ _ -> fm) nco (fromBool . not) (\ _ -> fm) p
+      co (BinOp l op r) = simplifyBinop l op r
+      nco ((:~:) p) = p
+      nco _ = fm
+      tf = fromBool
+      at _ = fm
+
+simplifyBinop :: forall p. (Constants p, Combinable p) => p -> BinOp -> p -> p
+simplifyBinop l op r =
+    case (asBool l, op, asBool r) of
+      (Just True,  (:&:), _         ) -> r
+      (Just False, (:&:), _         ) -> false
+      (_,          (:&:), Just True ) -> l
+      (_,          (:&:), Just False) -> false
+      (Just True,  (:|:), _         ) -> true
+      (Just False, (:|:), _         ) -> r
+      (_,          (:|:), Just True ) -> true
+      (_,          (:|:), Just False) -> l
+      (Just True,  (:=>:), _         ) -> r
+      (Just False, (:=>:), _         ) -> true
+      (_,          (:=>:), Just True ) -> true
+      (_,          (:=>:), Just False) -> (.~.) l
+      (Just False, (:<=>:), Just False) -> true
+      (Just True,  (:<=>:), _         ) -> r
+      (Just False, (:<=>:), _         ) -> (.~.) r
+      (_,          (:<=>:), Just True ) -> l
+      (_,          (:<=>:), Just False) -> (.~.) l
+      _ -> binop l op r
+
+simplify :: (FirstOrderFormula fof atom v,
+             Atom atom term v,
+             Term term v f) => fof -> fof
+simplify fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x fm' = simplify1 (quant op x (simplify fm'))
+      co ((:~:) fm') = simplify1 ((.~.) (simplify fm'))
+      co (BinOp fm1 op fm2) = simplify1 (binop (simplify fm1) op (simplify fm2))
+      tf = fromBool
+      at _ = fm
+
+-- | Just looks for double negatives and negated constants.
+lsimplify :: Literal lit atom => lit -> lit
+lsimplify fm = foldLiteral (lsimplify1 . (.~.) . lsimplify) fromBool (const fm) fm
+
+lsimplify1 :: Literal lit atom => lit -> lit
+lsimplify1 fm = foldLiteral (foldLiteral id (fromBool . not) (const fm)) fromBool (const fm) fm
+
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf :: FirstOrderFormula formula atom v => formula -> formula
+nnf fm =
+    foldFirstOrder nnfQuant nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfQuant op v p = quant op v (nnf p)
+      nnfCombine ((:~:) p) = foldFirstOrder nnfNotQuant nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf p .&. nnf q
+      nnfCombine (BinOp p (:|:) q) = nnf p .|. nnf q
+      nnfNotQuant Forall v p = exists v (nnf ((.~.) p))
+      nnfNotQuant Exists v p = for_all v (nnf ((.~.) p))
+      nnfNotCombine ((:~:) p) = nnf p
+      nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- ------------------------------------------------------------------------- 
+-- Prenex normal form.                                                       
+-- ------------------------------------------------------------------------- 
+
+pullQuants :: forall formula atom v term f. (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+              formula -> formula
+pullQuants fm =
+    foldFirstOrder (\ _ _ _ -> fm) pullQuantsCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      getQuant = foldFirstOrder (\ op v f -> Just (op, v, f)) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing)
+      pullQuantsCombine ((:~:) _) = fm
+      pullQuantsCombine (BinOp l op r) = 
+          case (getQuant l, op, getQuant r) of
+            (Just (Forall, vl, l'), (:&:), Just (Forall, vr, r')) -> pullq True  True  fm for_all (.&.) vl vr l' r'
+            (Just (Exists, vl, l'), (:|:), Just (Exists, vr, r')) -> pullq True  True  fm exists  (.|.) vl vr l' r'
+            (Just (Forall, vl, l'), (:&:), _)                     -> pullq True  False fm for_all (.&.) vl vl l' r
+            (_,                     (:&:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.&.) vr vr l  r'
+            (Just (Forall, vl, l'), (:|:), _)                     -> pullq True  False fm for_all (.|.) vl vl l' r
+            (_,                     (:|:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.|.) vr vr l  r'
+            (Just (Exists, vl, l'), (:&:), _)                     -> pullq True  False fm exists  (.&.) vl vl l' r
+            (_,                     (:&:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.&.) vr vr l  r'
+            (Just (Exists, vl, l'), (:|:), _)                     -> pullq True  False fm exists  (.|.) vl vl l' r
+            (_,                     (:|:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.|.) vr vr l  r'
+            _                                                     -> fm
+
+-- |Helper function to rename variables when we want to enclose a
+-- formula containing a free occurrence of that variable a quantifier
+-- that quantifies it.
+pullq :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) =>
+         Bool -> Bool
+      -> formula
+      -> (v -> formula -> formula)
+      -> (formula -> formula -> formula)
+      -> v -> v
+      -> formula -> formula
+      -> formula
+pullq l r fm mkq op x y p q =
+    let z = variant x (fv fm)
+        p' = if l then subst (x |=> vt z) p else p
+        q' = if r then subst (y |=> vt z) q else q
+        fm' = pullQuants (op p' q') in
+    mkq z fm'
+
+-- |Recursivly apply pullQuants anywhere a quantifier might not be
+-- leftmost.
+prenex :: (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+          formula -> formula 
+prenex fm =
+    foldFirstOrder qu co (\ _ -> fm) (\ _ -> fm) fm
+    where
+      qu op x p = quant op x (prenex p)
+      co (BinOp l (:&:) r) = pullQuants (prenex l .&. prenex r)
+      co (BinOp l (:|:) r) = pullQuants (prenex l .|. prenex r)
+      co _ = fm
+
+-- |Convert to Prenex normal form, with all quantifiers at the left.
+pnf :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) => formula -> formula
+pnf = prenex . nnf . simplify
+
+-- ------------------------------------------------------------------------- 
+-- Get the functions in a term and formula.                                  
+-- ------------------------------------------------------------------------- 
+
+-- FIXME: the function parameter should be a method in the Atom class,
+-- but we need to add a type parameter f to it first.
+functions :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- State monad for generating Skolem functions and constants.
+-- ------------------------------------------------------------------------- 
+
+-- | Harrison's code generated skolem functions by adding a prefix to
+-- the variable name they are based on.  Here we have a more general
+-- and type safe solution: we require that variables be instances of
+-- class Skolem which creates Skolem functions based on an integer.
+-- This state value exists in the SkolemT monad during skolemization
+-- and tracks the next available number and the current list of
+-- universally quantified variables.
+
+data SkolemState v term
+    = SkolemState
+      { skolemCount :: Int
+        -- ^ The next available Skolem number.
+      , univQuant :: [v]
+        -- ^ The variables which are universally quantified in the
+        -- current scope, in the order they were encountered.  During
+        -- Skolemization these are the parameters passed to the Skolem
+        -- function.
+      }
+
+-- | The state associated with the Skolem monad.
+newSkolemState :: SkolemState v term
+newSkolemState
+    = SkolemState
+      { skolemCount = 1
+      , univQuant = []
+      }
+
+-- | The Skolem monad transformer
+type SkolemT v term m = StateT (SkolemState v term) m
+
+-- | Run a computation in the Skolem monad.
+runSkolem :: SkolemT v term Identity a -> a
+runSkolem = runIdentity . runSkolemT
+
+-- | The Skolem monad
+type Skolem v term = StateT (SkolemState v term) Identity
+
+-- | Run a computation in a stacked invocation of the Skolem monad.
+runSkolemT :: Monad m => SkolemT v term m a -> m a
+runSkolemT action = (runStateT action) newSkolemState >>= return . fst
+
+-- ------------------------------------------------------------------------- 
+-- Core Skolemization function.                                              
+-- ------------------------------------------------------------------------- 
+
+-- |Skolemize the formula by removing the existential quantifiers and
+-- replacing the variables they quantify with skolem functions (and
+-- constants, which are functions of zero variables.)  The Skolem
+-- functions are new functions (obtained from the SkolemT monad) which
+-- are applied to the list of variables which are universally
+-- quantified in the context where the existential quantifier
+-- appeared.
+skolem :: (Monad m,
+           FirstOrderFormula fof atom v,
+           -- PropositionalFormula pf atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          fof -> SkolemT v term m fof
+skolem fm =
+    foldFirstOrder qu co (return . fromBool) (return . atomic) fm
+    where
+      -- We encountered an existentially quantified variable y,
+      -- allocate a new skolem function fx and do a substitution to
+      -- replace occurrences of y with fx.  The value of the Skolem
+      -- function is assumed to equal the value of y which satisfies
+      -- the formula.
+      qu Exists y p =
+          do let xs = fv fm
+             let fx = fApp (C.toSkolem y) (map vt (Set.toAscList xs))
+             skolem (subst (Map.singleton y fx) p)
+      qu Forall x p = skolem p >>= return . for_all x
+      co (BinOp l (:&:) r) = skolem2 (.&.) l r
+      co (BinOp l (:|:) r) = skolem2 (.|.) l r
+      co _ = return fm
+
+skolem2 :: (Monad m,
+            FirstOrderFormula fof atom v,
+            -- PropositionalFormula pf atom,
+            -- Formula formula term v,
+            Atom atom term v,
+            Term term v f) =>
+           (fof -> fof -> fof) -> fof -> fof -> SkolemT v term m fof
+skolem2 cons p q =
+    skolem p >>= \ p' ->
+    skolem q >>= \ q' ->
+    return (cons p' q')
+
+-- ------------------------------------------------------------------------- 
+-- Overall Skolemization function.                                           
+-- ------------------------------------------------------------------------- 
+
+-- |I need to consult the Harrison book for the reasons why we don't
+-- |just Skolemize the result of prenexNormalForm.
+askolemize :: forall m fof atom term v f.
+              (Monad m,
+               FirstOrderFormula fof atom v,
+               Atom atom term v,
+               Term term v f) =>
+              fof -> SkolemT v term m fof
+askolemize = skolem . nnf . simplify
+
+-- | Remove the leading universal quantifiers.  After a call to pnf
+-- this will be all the universal quantifiers, and the skolemization
+-- will have already turned all the existential quantifiers into
+-- skolem functions.
+specialize :: forall fof atom v. FirstOrderFormula fof atom v => fof -> fof
+specialize f =
+    foldFirstOrder q (\ _ -> f) (\ _ -> f) (\ _ -> f) f
+    where
+      q Forall _ f' = specialize f'
+      q _ _ _ = f
+
+-- | Skolemize and then specialize.  Because we know all quantifiers
+-- are gone we can convert to any instance of PropositionalFormula.
+skolemize :: forall m fof atom term v f pf atom2. (Monad m,
+              FirstOrderFormula fof atom v,
+              PropositionalFormula pf atom2,
+              Atom atom term v,
+              Term term v f,
+              Eq pf) =>
+             (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemize ca fm = askolemize fm >>= return . (toPropositional ca :: fof -> pf) . specialize . pnf
+
+{-
+-- | Convert a first order formula into a disjunct of conjuncts of
+-- literals.  Note that this can convert any instance of
+-- FirstOrderFormula into any instance of Literal.
+literal :: forall fof atom term v p f lit. (Literal fof atom, Apply atom p term, Term term v f, Literal lit atom, Formula lit atom, Ord lit) =>
+           fof -> Set.Set (Set.Set lit)
+literal fm =
+    foldLiteral neg tf at fm
+    where
+      neg :: fof -> Set.Set (Set.Set lit)
+      neg x = Set.map (Set.map (.~.)) (literal x)
+      tf = Set.singleton . Set.singleton . fromBool
+      at :: atom -> Set.Set (Set.Set lit)
+      at x = foldApply (\ _ _ -> Set.singleton (Set.singleton (atomic x))) tf x
+-}
+
+-- |We get Skolem Normal Form by skolemizing and then converting to
+-- Prenex Normal Form, and finally eliminating the remaining quantifiers.
+skolemNormalForm :: (FirstOrderFormula fof atom v,
+                     PropositionalFormula pf atom2,
+                     -- Formula fof term v,
+                     -- Formula pf term v,
+                     Atom atom term v,
+                     Term term v f,
+                     Monad m, Ord fof, Eq pf) =>
+                    (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemNormalForm = skolemize
diff --git a/testdata/split-expected/Data/Logic/Harrison/Tableaux.hs b/testdata/split-expected/Data/Logic/Harrison/Tableaux.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Tableaux.hs
@@ -0,0 +1,624 @@
+{-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Tableaux
+    ( unify_literals
+    , unifyAtomsEq
+    , deepen
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(freeVariables, substitute, unify))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Literal.ZipLiterals (zipLiterals)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(..), vt)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib', settryfind)
+import Data.Logic.Harrison.Prop (simpdnf)
+import Data.Logic.Harrison.Skolem (runSkolem, skolemize)
+import Data.Logic.Harrison.Unif (unify)
+import qualified Data.Map as Map (empty, fromList, Map)
+import qualified Data.Set as Set (empty, map, minView, partition, Set, singleton, toList, union)
+import Data.String (IsString(..))
+import Debug.Trace (trace)
+
+-- =========================================================================
+-- Tableaux, seen as an optimized version of a Prawitz-like procedure.       
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Unify literals (just pretend the toplevel relation is a function).        
+-- ------------------------------------------------------------------------- 
+
+unify_literals :: forall lit atom term v f.
+                  (Literal lit atom,
+                   C.Atom atom term v,
+                   Term term v f) =>
+                  Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_literals env f1 f2 =
+    maybe err id (zipLiterals co tf at f1 f2)
+    where
+      -- co :: lit -> lit -> Maybe (Failing (Map.Map v term))
+      co p q = Just $ unify_literals env p q
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      at :: atom -> atom -> Maybe (Failing (Map.Map v term))
+      at a1 a2 = Just $ C.unify env a1 a2
+      err = Failure ["Can't unify literals"]
+
+unifyAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+unifyAtomsEq env a1 a2 =
+    maybe err id (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p1 ts1 p2 ts2 =
+          if p1 == p2 && length ts1 == length ts2
+          then Just $ unify env (zip ts1 ts2)
+          else Nothing
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      eq pl pr ql qr = Just $ unify env [(pl, ql), (pr, qr)]
+      err = Failure ["Can't unify atoms"]
+
+-- ------------------------------------------------------------------------- 
+-- Unify complementary literals.                                             
+-- ------------------------------------------------------------------------- 
+
+unify_complements :: forall lit atom term v f.
+                     (Literal lit atom,
+                      C.Atom atom term v,
+                      Term term v f) =>
+                     Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_complements env p q = unify_literals env p ((.~.) q)
+
+-- ------------------------------------------------------------------------- 
+-- Unify and refute a set of disjuncts.                                      
+-- ------------------------------------------------------------------------- 
+
+unify_refute :: (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) => Set.Set (Set.Set lit) -> Map.Map v term -> Failing (Map.Map v term)
+unify_refute djs env =
+    case Set.minView djs of
+      Nothing -> Success env
+      Just (d, odjs) ->
+          settryfind (\ (p, n) -> unify_complements env p n >>= unify_refute odjs) pairs
+          where
+            pairs = allpairs (,) pos neg
+            (pos,neg) = Set.partition positive d
+
+-- ------------------------------------------------------------------------- 
+-- Hence a Prawitz-like procedure (using unification on DNF).                
+-- ------------------------------------------------------------------------- 
+
+prawitz_loop :: forall atom v term f lit. (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit) -> [v] -> Set.Set (Set.Set lit) -> Int -> (Map.Map v term, Int)
+prawitz_loop djs0 fvs djs n =
+    let l = length fvs in
+    let newvars = map (\ k -> fromString ("_" ++ show (n * l + k))) [1..l] in
+    let inst = Map.fromList (zip fvs (map vt newvars)) in
+    let djs1 = distrib' (Set.map (Set.map (mapAtoms (atomic . substitute' inst))) djs0) djs in
+    case unify_refute djs1 Map.empty of
+      Failure _ -> prawitz_loop djs0 fvs djs1 (n + 1)
+      Success env -> (env, n + 1)
+    where
+      substitute' :: Map.Map v term -> atom -> atom
+      substitute' = C.substitute
+
+-- prawitz :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Int
+prawitz :: forall fof atom term v f lit pf.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal lit atom,
+            Term term v f,
+            C.Atom atom term v) =>
+           fof -> Int
+prawitz fm =
+    snd (prawitz_loop dnf (Set.toList fvs) dnf0 0 :: (Map.Map v term, Int))
+    where
+      dnf0 = (Set.singleton Set.empty) :: Set.Set (Set.Set lit)
+      dnf = simpdnf pf :: Set.Set (Set.Set lit)
+      fvs = foldAtoms (\ s (a :: atom) -> Set.union (C.freeVariables a) s) Set.empty pf :: Set.Set v
+      pf = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+test01 = TestCase $ assertEqual "p20 - prawitz" expected input
+    where input = prawitz fm
+          fm = (for_all "x" (for_all "y" (exists "z" (for_all "w" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"] .=>.
+                                                                   pApp "R" [vt "z"] .&. pApp "U" [vt "w"]))))) .=>.
+               (exists "x" (exists "y" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"]))) .=>. (exists "z" (pApp "R" [vt "z"]))
+          expected = 1
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Comparison of number of ground instances.                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+compare :: forall fof pf lit atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            C.Atom atom term v,
+            IsString f) =>
+           (atom -> Set.Set (f, Int)) -> fof -> (Int, Failing Int)
+-}
+-- compare fa fm = (prawitz fm, davisputnam fa fm)
+{-
+START_INTERACTIVE;;
+test02 = TestCase $ assertEqual "p19" expected input
+    where input = compare (exists "x" (forall "y" (for_all "z" ((pApp "P" [vt "y"] .=>. pApp "Q" [vt "z"]) .=>. pApp "P" [vt "x"] .=>. pApp "Q" [vt "x"]))))
+
+let p20 = compare
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p24 = compare
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"])
+   .=>. (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p39 = compare
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p42 = compare
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+{- **** Too slow?
+
+let p43 = compare
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+ ***** -}
+
+let p44 = compare
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y)))
+   .=>. (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p59 = compare
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = compare
+ <<forall x. P(x,f[vt "x"]) .<=>.
+             exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- More standard tableau procedure, effectively doing DNF incrementally.     
+-- ------------------------------------------------------------------------- 
+
+let rec tableau (fms,lits,n) cont (env,k) =
+  if n < 0 then error "no proof at this level" else
+  match fms with
+    [] -> error "tableau: no proof"
+  | And(p,q) : unexp ->
+      tableau (p : q : unexp,lits,n) cont (env,k)
+  | Or(p,q) : unexp ->
+      tableau (p : unexp,lits,n) (tableau (q : unexp,lits,n) cont) (env,k)
+  | Forall(x,p) : unexp ->
+      let y = Vt("_" ++ string_of_int k) in
+      let p' = subst (x |=> y) p in
+      tableau (p' : unexp@[Forall(x,p)],lits,n-1) cont (env,k+1)
+  | fm : unexp ->
+      try tryfind (\ l -> cont(unify_complements env (fm,l),k)) lits
+      with Failure _ -> tableau (unexp,fm : lits,n) cont (env,k);;
+-}
+
+-- | Try f with higher and higher values of n until it succeeds, or
+-- optional maximum depth limit is exceeded.
+deepen :: (Int -> Failing t) -> Int -> Maybe Int -> Failing (t, Int)
+deepen _ n (Just m) | n > m = Failure ["Exceeded maximum depth limit"]
+deepen f n m =
+    -- If no maximum depth limit is given print a trace of the
+    -- levels tried.  The assumption is that we are running
+    -- interactively.
+    let n' = maybe (trace ("Searching with depth limit " ++ show n) n) (const n) m in
+    case f n' of
+      Failure _ -> deepen f (n + 1) m
+      Success x -> Success (x, n)
+
+{-
+let tabrefute fms =
+  deepen (\ n -> tableau (fms,[],n) (\ x -> x) (Map.empty,0); n) 0;;
+
+let tab fm =
+  let sfm = askolemize(Not(generalize fm)) in
+  if sfm = False then 0 else tabrefute [sfm];;
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p38 = tab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Try to split up the initial formula first; often a big improvement.       
+-- ------------------------------------------------------------------------- 
+
+let splittab fm = 
+  map tabrefute (simpdnf(askolemize(Not(generalize fm))));;
+
+-- ------------------------------------------------------------------------- 
+-- Example: the Andrews challenge.                                           
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p34 = splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Another nice example from EWD 1602.                                       
+-- ------------------------------------------------------------------------- 
+
+let ewd1062 = splittab
+ <<(forall x. x <= x) .&.
+   (forall x y z. x <= y .&. y <= z .=>. x <= z) .&.
+   (forall x y. f[vt "x"] <= y .<=>. x <= g[vt "y"])
+   .=>. (forall x y. x <= y .=>. f[vt "x"] <= f[vt "y"]) .&.
+       (forall x y. x <= y .=>. g[vt "x"] <= g[vt "y"])>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Do all the equality-free Pelletier problems, and more, as examples.       
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time splittab
+ <<p .=>. q .<=>. ~q .=>. ~p>>;;
+
+let p2 = time splittab
+ <<~ ~p .<=>. p>>;;
+
+let p3 = time splittab
+ <<~(p .=>. q) .=>. q .=>. p>>;;
+
+let p4 = time splittab
+ <<~p .=>. q .<=>. ~q .=>. p>>;;
+
+let p5 = time splittab
+ <<(p .|. q .=>. p .|. r) .=>. p .|. (q .=>. r)>>;;
+
+let p6 = time splittab
+ <<p .|. ~p>>;;
+
+let p7 = time splittab
+ <<p .|. ~ ~ ~p>>;;
+
+let p8 = time splittab
+ <<((p .=>. q) .=>. p) .=>. p>>;;
+
+let p9 = time splittab
+ <<(p .|. q) .&. (~p .|. q) .&. (p .|. ~q) .=>. ~(~q .|. ~q)>>;;
+
+let p10 = time splittab
+ <<(q .=>. r) .&. (r .=>. p .&. q) .&. (p .=>. q .&. r) .=>. (p .<=>. q)>>;;
+
+let p11 = time splittab
+ <<p .<=>. p>>;;
+
+let p12 = time splittab
+ <<((p .<=>. q) .<=>. r) .<=>. (p .<=>. (q .<=>. r))>>;;
+
+let p13 = time splittab
+ <<p .|. q .&. r .<=>. (p .|. q) .&. (p .|. r)>>;;
+
+let p14 = time splittab
+ <<(p .<=>. q) .<=>. (q .|. ~p) .&. (~q .|. p)>>;;
+
+let p15 = time splittab
+ <<p .=>. q .<=>. ~p .|. q>>;;
+
+let p16 = time splittab
+ <<(p .=>. q) .|. (q .=>. p)>>;;
+
+let p17 = time splittab
+ <<p .&. (q .=>. r) .=>. s .<=>. (~p .|. q .|. s) .&. (~p .|. ~r .|. s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletier problems: monadic predicate logic.                              
+-- ------------------------------------------------------------------------- 
+
+let p18 = time splittab
+ <<exists y. forall x. P[vt "y"] .=>. P[vt "x"]>>;;
+
+let p19 = time splittab
+ <<exists x. forall y z. (P[vt "y"] .=>. Q[vt "z"]) .=>. P[vt "x"] .=>. Q[vt "x"]>>;;
+
+let p20 = time splittab
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p21 = time splittab
+ <<(exists x. P .=>. Q[vt "x"]) .&. (exists x. Q[vt "x"] .=>. P)
+   .=>. (exists x. P .<=>. Q[vt "x"])>>;;
+
+let p22 = time splittab
+ <<(forall x. P .<=>. Q[vt "x"]) .=>. (P .<=>. (forall x. Q[vt "x"]))>>;;
+
+let p23 = time splittab
+ <<(forall x. P .|. Q[vt "x"]) .<=>. P .|. (forall x. Q[vt "x"])>>;;
+
+let p24 = time splittab
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"]) .=>.
+   (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p25 = time splittab
+ <<(exists x. P[vt "x"]) .&.
+   (forall x. U[vt "x"] .=>. ~G[vt "x"] .&. R[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. G[vt "x"] .&. U[vt "x"]) .&.
+   ((forall x. P[vt "x"] .=>. Q[vt "x"]) .|. (exists x. Q[vt "x"] .&. P[vt "x"]))
+   .=>. (exists x. Q[vt "x"] .&. P[vt "x"])>>;;
+
+let p26 = time splittab
+ <<((exists x. P[vt "x"]) .<=>. (exists x. Q[vt "x"])) .&.
+   (forall x y. P[vt "x"] .&. Q[vt "y"] .=>. (R[vt "x"] .<=>. U[vt "y"]))
+   .=>. ((forall x. P[vt "x"] .=>. R[vt "x"]) .<=>. (forall x. Q[vt "x"] .=>. U[vt "x"]))>>;;
+
+let p27 = time splittab
+ <<(exists x. P[vt "x"] .&. ~Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. R[vt "x"]) .&.
+   (forall x. U[vt "x"] .&. V[vt "x"] .=>. P[vt "x"]) .&.
+   (exists x. R[vt "x"] .&. ~Q[vt "x"])
+   .=>. (forall x. U[vt "x"] .=>. ~R[vt "x"])
+       .=>. (forall x. U[vt "x"] .=>. ~V[vt "x"])>>;;
+
+let p28 = time splittab
+ <<(forall x. P[vt "x"] .=>. (forall x. Q[vt "x"])) .&.
+   ((forall x. Q[vt "x"] .|. R[vt "x"]) .=>. (exists x. Q[vt "x"] .&. R[vt "x"])) .&.
+   ((exists x. R[vt "x"]) .=>. (forall x. L[vt "x"] .=>. M[vt "x"])) .=>.
+   (forall x. P[vt "x"] .&. L[vt "x"] .=>. M[vt "x"])>>;;
+
+let p29 = time splittab
+ <<(exists x. P[vt "x"]) .&. (exists x. G[vt "x"]) .=>.
+   ((forall x. P[vt "x"] .=>. H[vt "x"]) .&. (forall x. G[vt "x"] .=>. J[vt "x"]) .<=>.
+    (forall x y. P[vt "x"] .&. G[vt "y"] .=>. H[vt "x"] .&. J[vt "y"]))>>;;
+
+let p30 = time splittab
+ <<(forall x. P[vt "x"] .|. G[vt "x"] .=>. ~H[vt "x"]) .&.
+   (forall x. (G[vt "x"] .=>. ~U[vt "x"]) .=>. P[vt "x"] .&. H[vt "x"])
+   .=>. (forall x. U[vt "x"])>>;;
+
+let p31 = time splittab
+ <<~(exists x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"])) .&.
+   (exists x. Q[vt "x"] .&. P[vt "x"]) .&.
+   (forall x. ~H[vt "x"] .=>. J[vt "x"])
+   .=>. (exists x. Q[vt "x"] .&. J[vt "x"])>>;;
+
+let p32 = time splittab
+ <<(forall x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"]) .=>. Q[vt "x"]) .&.
+   (forall x. Q[vt "x"] .&. H[vt "x"] .=>. J[vt "x"]) .&.
+   (forall x. R[vt "x"] .=>. H[vt "x"])
+   .=>. (forall x. P[vt "x"] .&. R[vt "x"] .=>. J[vt "x"])>>;;
+
+let p33 = time splittab
+ <<(forall x. P[vt "a"] .&. (P[vt "x"] .=>. P[vt "b"]) .=>. P[vt "c"]) .<=>.
+   (forall x. P[vt "a"] .=>. P[vt "x"] .|. P[vt "c"]) .&. (P[vt "a"] .=>. P[vt "b"] .=>. P[vt "c"])>>;;
+
+let p34 = time splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+let p35 = time splittab
+ <<exists x y. P(x,y) .=>. (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Full predicate logic (without identity and functions).                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time splittab
+ <<(forall x. exists y. P(x,y)) .&.
+   (forall x. exists y. G(x,y)) .&.
+   (forall x y. P(x,y) .|. G(x,y)
+   .=>. (forall z. P(y,z) .|. G(y,z) .=>. H(x,z)))
+       .=>. (forall x. exists y. H(x,y))>>;;
+
+let p37 = time splittab
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) .=>. P(y,w)) .&. P(y,z) .&.
+     (P(y,w) .=>. (exists u. Q(u,w)))) .&.
+   (forall x z. ~P(x,z) .=>. (exists y. Q(y,z))) .&.
+   ((exists x y. Q(x,y)) .=>. (forall x. R(x,x))) .=>.
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time splittab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+
+let p39 = time splittab
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p40 = time splittab
+ <<(exists y. forall x. P(x,y) .<=>. P(x,x))
+  .=>. ~(forall x. exists y. forall z. P(z,y) .<=>. ~P(z,x))>>;;
+
+let p41 = time splittab
+ <<(forall z. exists y. forall x. P(x,y) .<=>. P(x,z) .&. ~P(x,x))
+  .=>. ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time splittab
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+let p43 = time splittab
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+let p44 = time splittab
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y))) .=>.
+   (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p45 = time splittab
+ <<(forall x.
+     P[vt "x"] .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y)) .=>.
+       (forall y. G[vt "y"] .&. H(x,y) .=>. R[vt "y"])) .&.
+   ~(exists y. L[vt "y"] .&. R[vt "y"]) .&.
+   (exists x. P[vt "x"] .&. (forall y. H(x,y) .=>.
+     L[vt "y"]) .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y))) .=>.
+   (exists x. P[vt "x"] .&. ~(exists y. G[vt "y"] .&. H(x,y)))>>;;
+
+let p46 = time splittab
+ <<(forall x. P[vt "x"] .&. (forall y. P[vt "y"] .&. H(y,x) .=>. G[vt "y"]) .=>. G[vt "x"]) .&.
+   ((exists x. P[vt "x"] .&. ~G[vt "x"]) .=>.
+    (exists x. P[vt "x"] .&. ~G[vt "x"] .&.
+               (forall y. P[vt "y"] .&. ~G[vt "y"] .=>. J(x,y)))) .&.
+   (forall x y. P[vt "x"] .&. P[vt "y"] .&. H(x,y) .=>. ~J(y,x)) .=>.
+   (forall x. P[vt "x"] .=>. G[vt "x"])>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Well-known "Agatha" example; cf. Manthey and Bry, CADE-9.                 
+-- ------------------------------------------------------------------------- 
+
+let p55 = time splittab
+ <<lives(agatha) .&. lives(butler) .&. lives(charles) .&.
+   (killed(agatha,agatha) .|. killed(butler,agatha) .|.
+    killed(charles,agatha)) .&.
+   (forall x y. killed(x,y) .=>. hates(x,y) .&. ~richer(x,y)) .&.
+   (forall x. hates(agatha,x) .=>. ~hates(charles,x)) .&.
+   (hates(agatha,agatha) .&. hates(agatha,charles)) .&.
+   (forall x. lives[vt "x"] .&. ~richer(x,agatha) .=>. hates(butler,x)) .&.
+   (forall x. hates(agatha,x) .=>. hates(butler,x)) .&.
+   (forall x. ~hates(x,agatha) .|. ~hates(x,butler) .|. ~hates(x,charles))
+   .=>. killed(agatha,agatha) .&.
+       ~killed(butler,agatha) .&.
+       ~killed(charles,agatha)>>;;
+
+let p57 = time splittab
+ <<P(f([vt "a"],b),f(b,c)) .&.
+   P(f(b,c),f(a,c)) .&.
+   (forall [vt "x"] y z. P(x,y) .&. P(y,z) .=>. P(x,z))
+   .=>. P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time splittab
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P[vt "x"] .&. Q[vt "y"]) .=>. ((P[vt "v"] .|. R[vt "w"])  .&. (R[vt "z"] .=>. Q[vt "v"])))>>;;
+
+let p59 = time splittab
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = time splittab
+ <<forall x. P(x,f[vt "x"]) .<=>.
+            exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **** This is still too hard for us! Amazing...
+
+let gilmore_1 = time splittab
+ <<exists x. forall y z.
+      ((F[vt "y"] .=>. G[vt "y"]) .<=>. F[vt "x"]) .&.
+      ((F[vt "y"] .=>. H[vt "y"]) .<=>. G[vt "x"]) .&.
+      (((F[vt "y"] .=>. G[vt "y"]) .=>. H[vt "y"]) .<=>. H[vt "x"])
+      .=>. F[vt "z"] .&. G[vt "z"] .&. H[vt "z"]>>;;
+
+ ***** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time splittab
+ <<exists x y. forall z.
+        (F(x,z) .<=>. F(z,y)) .&. (F(z,y) .<=>. F(z,z)) .&. (F(x,y) .<=>. F(y,x))
+        .=>. (F(x,y) .<=>. F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. H[vt "x"])) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. H[vt "z"]) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_4 = time splittab
+ <<exists x y. forall z.
+        (F(x,y) .=>. F(y,z) .&. F(z,z)) .&.
+        (F(x,y) .&. G(x,y) .=>. G(x,z) .&. G(z,z))>>;;
+
+let gilmore_5 = time splittab
+ <<(forall x. exists y. F(x,y) .|. F(y,x)) .&.
+   (forall x y. F(y,x) .=>. F(y,y))
+   .=>. exists z. F(z,z)>>;;
+
+let gilmore_6 = time splittab
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) .=>. G(v,u) .&. G(u,x))
+        .=>. (exists u. forall v. F(u,y) .=>. G(v,u) .&. G(u,y)) .|.
+            (forall u v. exists w. G(v,u) .|. H(w,y,u) .=>. G(u,w))>>;;
+
+let gilmore_7 = time splittab
+ <<(forall x. K[vt "x"] .=>. exists y. L[vt "y"] .&. (F(x,y) .=>. G(x,y))) .&.
+   (exists z. K[vt "z"] .&. forall u. L[vt "u"] .=>. F(z,u))
+   .=>. exists v w. K[vt "v"] .&. L[vt "w"] .&. G(v,w)>>;;
+
+let gilmore_8 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. (forall u. exists v. H(u,v,x)))) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. (forall u. exists v. H(u,v,z))) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_9 = time splittab
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))) .&.
+        ((forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))
+         .=>. ~(forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+         .=>. (forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x)) .&.
+             (forall u. exists v. F(z,u,v) .&. G(y,u) .&. ~H(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time splittab
+ <<exists x. exists y. forall z.
+        (F(x,y) .=>. (F(y,z) .&. F(z,z))) .&.
+        ((F(x,y) .&. G(x,y)) .=>. (G(x,z) .&. G(z,z)))>>;;
+
+************ -}
+
+-}
diff --git a/testdata/split-expected/Data/Logic/Harrison/Unif.hs b/testdata/split-expected/Data/Logic/Harrison/Unif.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Harrison/Unif.hs
@@ -0,0 +1,125 @@
+{-# OPTIONS -Wall #-}
+module Data.Logic.Harrison.Unif
+    ( unify
+    , solve
+    , fullUnify
+    , unifyAndApply
+    ) where
+
+import Data.Logic.Classes.Term (Term(..), tsubst)
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+{-
+(* ========================================================================= *)
+(* Unification for first order terms.                                        *)
+(*                                                                           *)
+(* Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  *)
+(* ========================================================================= *)
+
+let rec istriv env x t =
+  match t with
+    Var y -> y = x or defined env y & istriv env x (apply env y)
+  | Fn(f,args) -> exists (istriv env x) args & failwith "cyclic";;
+-}
+isTrivial :: Term term v f => Map.Map v term -> v -> term -> Failing Bool
+isTrivial env x t =
+    foldTerm v f t
+    where
+      v y =
+          if x == y
+          then Success True
+          else maybe (Success False) (isTrivial env x) (Map.lookup y env)
+      f _ args =
+          if any (failing (const False) id . isTrivial env x) args
+          then Failure ["cyclic"]
+          else Success False
+
+{-
+    foldT (\ y -> y == x || (defined env y && istriv env x (apply env y)))
+          (\ _ args -> if any (istriv env x) args then error "cyclic" else False)
+          t
+-}
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Main unification procedure                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec unify env eqs =
+  match eqs with
+    [] -> env
+  | (Fn(f,fargs),Fn(g,gargs))::oth ->
+        if f = g & length fargs = length gargs
+        then unify env (zip fargs gargs @ oth)
+        else failwith "impossible unification"
+  | (Var x,t)::oth | (t,Var x)::oth ->
+        if defined env x then unify env ((apply env x,t)::oth)
+        else unify (if istriv env x t then env else (x|->t) env) oth;;
+-}
+unify :: Term term v f => Map.Map v term -> [(term,term)] -> Failing (Map.Map v term)
+unify env [] = Success env
+unify env ((a,b):oth) =
+    foldTerm (vr b) (\ f fargs -> foldTerm (vr a) (fn f fargs) b) a
+    where
+      vr t x =
+          maybe (isTrivial env x t >>= \ trivial -> unify (if trivial then env else Map.insert x t env) oth)
+                (\ y -> unify env ((y, t) : oth))
+                (Map.lookup x env)
+      fn f fargs g gargs =
+          if f == g && length fargs == length gargs
+          then unify env (zip fargs gargs ++ oth)
+          else Failure ["impossible unification"]
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Solve to obtain a single instantiation.                                   *)
+(* ------------------------------------------------------------------------- *)
+
+let rec solve env =
+  let env' = mapf (tsubst env) env in
+  if env' = env then env else solve env';;
+-}
+solve :: Term term v f => Map.Map v term -> Map.Map v term
+solve env =
+    if env' == env then env else solve env'
+    where env' = Map.map (tsubst env) env
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Unification reaching a final solved form (often this isn't needed).       *)
+(* ------------------------------------------------------------------------- *)
+
+let fullunify eqs = solve (unify undefined eqs);;
+-}
+fullUnify :: Term term v f => [(term,term)] -> Failing (Map.Map v term)
+fullUnify eqs = failing Failure (Success . solve) (unify Map.empty eqs)
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Examples.                                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let unify_and_apply eqs =
+  let i = fullunify eqs in
+  let apply (t1,t2) = tsubst i t1,tsubst i t2 in
+  map apply eqs;;
+-}
+unifyAndApply :: Term term v f => [(term, term)] -> Failing [(term, term)]
+unifyAndApply eqs =
+    case fullUnify eqs of
+      Failure x -> Failure x
+      Success i -> Success (map (\ (t1, t2) -> (tsubst i t1, tsubst i t2)) eqs)
+{-
+
+START_INTERACTIVE;;
+unify_and_apply [<<|f(x,g(y))|>>,<<|f(f(z),w)|>>];;
+
+unify_and_apply [<<|f(x,y)|>>,<<|f(y,x)|>>];;
+
+(****  unify_and_apply [<<|f(x,g(y))|>>,<<|f(y,x)|>>];; *****)
+
+unify_and_apply [<<|x_0|>>,<<|f(x_1,x_1)|>>;
+                 <<|x_1|>>,<<|f(x_2,x_2)|>>;
+                 <<|x_2|>>,<<|f(x_3,x_3)|>>];;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-expected/Data/Logic/Instances/Chiou.hs b/testdata/split-expected/Data/Logic/Instances/Chiou.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Instances/Chiou.hs
@@ -0,0 +1,307 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             RankNTypes, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall -Wwarn -fno-warn-orphans -fno-warn-missing-signatures #-}
+module Data.Logic.Instances.Chiou
+    ( Sentence(..)
+    , CTerm(..)
+    , Connective(..)
+    , Quantifier(..)
+    , ConjunctiveNormalForm(..)
+    , NormalSentence(..)
+    , NormalTerm(..)
+    , toSentence
+    , fromSentence
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (Combinable(..), BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..), asBool, true, false)
+import Data.Logic.Classes.Equals (AtomEq(..), (.=.))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..), quant', pApp, prettyFirstOrder, fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..), (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..))
+import Data.Logic.Classes.Term (Term(..), Function)
+import Data.Logic.Classes.Variable (Variable)
+import qualified Data.Logic.Classes.FirstOrder as L
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.String (IsString(..))
+
+data Sentence v p f
+    = Connective (Sentence v p f) Connective (Sentence v p f)
+    | Quantifier Quantifier [v] (Sentence v p f)
+    | Not (Sentence v p f)
+    | Predicate p [CTerm v f]
+    | Equal (CTerm v f) (CTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+data CTerm v f
+    = Function f [CTerm v f]
+    | Variable v
+    deriving (Eq, Ord, Data, Typeable)
+
+data Connective
+    = Imply
+    | Equiv
+    | And
+    | Or
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+data Quantifier
+    = ForAll
+    | ExistsCh
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Negatable (Sentence v p f) where
+    negatePrivate = Not
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Constants p, Eq (Sentence v p f)) => Constants (Sentence v p f) where
+    fromBool x = Predicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance ({- Constants (Sentence v p f), -} Ord v, Ord p, Ord f) => Combinable (Sentence v p f) where
+    x .<=>. y = Connective x Equiv y
+    x .=>.  y = Connective x Imply y
+    x .|.   y = Connective x Or y
+    x .&.   y = Connective x And y
+
+instance (Predicate p, Function f v) => Formula (Sentence v p f) (Sentence v p f) where
+    atomic (Connective _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Quantifier _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Not _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic x@(Predicate _ _) = x
+    atomic x@(Equal _ _) = x
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f), Variable v, Predicate p, Function f v, Combinable (Sentence v p f)) =>
+         PropositionalFormula (Sentence v p f) (Sentence v p f) where
+    foldPropositional co tf at formula =
+        case formula of
+          Not x -> co ((:~:) x)
+          Quantifier _ _ _ -> error "Logic.Instance.Chiou.foldF0: unexpected"
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate p ts -> maybe (at (Predicate p ts)) tf (asBool p)
+          Equal t1 t2 -> at (Equal t1 t2)
+
+data AtomicFunction v
+    = AtomicFunction String
+    -- This is redundant with the SkolemFunction and SkolemConstant
+    -- constructors in the Chiou Term type.
+    | AtomicSkolemFunction v
+    deriving (Eq, Show)
+
+instance IsString (AtomicFunction v) where
+    fromString = AtomicFunction
+
+instance Variable v => Skolem (AtomicFunction v) v where
+    toSkolem = AtomicSkolemFunction
+    isSkolem (AtomicSkolemFunction _) = True
+    isSkolem _ = False
+
+-- The Atom type is not cleanly distinguished from the Sentence type, so we need an Atom instance for Sentence.
+instance (Variable v, Predicate p, Function f v) => Apply (Sentence v p f) p (CTerm v f) where
+    foldApply ap tf (Predicate p ts) = maybe (ap p ts) tf (asBool p)
+    foldApply _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    apply' = Predicate
+
+instance Predicate p => AtomEq (Sentence v p f) p (CTerm v f) where
+    foldAtomEq ap tf _ (Predicate p ts) = if p == true then tf True else if p == false then tf False else ap p ts
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    foldAtomEq _ _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    equals = Equal
+    applyEq' = Predicate
+
+instance (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Variable v, Predicate p, Function f v) => Pretty (Sentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Formula (Sentence v p f) (Sentence v p f), Predicate p, Function f v, Variable v) => HasFixity (Sentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f),
+          Variable v, Predicate p, Function f v) =>
+          FirstOrderFormula (Sentence v p f) (Sentence v p f) v where
+    for_all v x = Quantifier ForAll [v] x
+    exists v x = Quantifier ExistsCh [v] x
+    foldFirstOrder qu co tf at f =
+        case f of
+          Not x -> co ((:~:) x)
+          Quantifier op (v:vs) f' ->
+              let op' = case op of
+                          ForAll -> Forall
+                          ExistsCh -> Exists in
+              -- Use Logic.quant' here instead of the constructor
+              -- Quantifier so as not to create quantifications with
+              -- empty variable lists.
+              qu op' v (quant' op' vs f')
+          Quantifier _ [] f' -> foldFirstOrder qu co tf at f'
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate _ _ -> at f
+          Equal _ _ -> at f
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Not f1', Not f2') -> co ((:~:) f1') ((:~:) f2')
+          (Quantifier op1 (v1:vs1) f1', Quantifier op2 (v2:vs2) f2') ->
+              if op1 == op2
+              then let op' = case op1 of
+                               ForAll -> Forall
+                               ExistsCh -> Exists in
+                   qu op' v1 (Quantifier op1 vs1 f1') Forall v2 (Quantifier op2 vs2 f2')
+              else Nothing
+          (Quantifier q1 [] f1', Quantifier q2 [] f2') ->
+              if q1 == q2 then zipFirstOrder qu co tf at f1' f2' else Nothing
+          (Connective l1 op1 r1, Connective l2 op2 r2) ->
+              case (op1, op2) of
+                (And, And) -> co (BinOp l1 (:&:) r1) (BinOp l2 (:&:) r2)
+                (Or, Or) -> co (BinOp l1 (:|:) r1) (BinOp l2 (:|:) r2)
+                (Imply, Imply) -> co (BinOp l1 (:=>:) r1) (BinOp l2 (:=>:) r2)
+                (Equiv, Equiv) -> co (BinOp l1 (:<=>:) r1) (BinOp l2 (:<=>:) r2)
+                _ -> Nothing
+          (Equal _ _, Equal _ _) -> at f1 f2
+          (Predicate _ _, Predicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Variable v, Function f v) => Term (CTerm v f) v f where
+    foldTerm v fn t =
+        case t of
+          Variable x -> v x
+          Function f ts -> fn f ts
+    zipTerms  v f t1 t2 =
+        case (t1, t2) of
+          (Variable v1, Variable v2) -> v v1 v2
+          (Function f1 ts1, Function f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Variable
+    fApp f ts = Function f ts
+
+data ConjunctiveNormalForm v p f =
+    CNF [Sentence v p f]
+    deriving (Eq)
+
+data NormalSentence v p f
+    = NFNot (NormalSentence v p f)
+    | NFPredicate p [NormalTerm v f]
+    | NFEqual (NormalTerm v f) (NormalTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+-- We need a distinct type here because of the functional dependencies
+-- in class FirstOrderFormula.
+data NormalTerm v f
+    = NormalFunction f [NormalTerm v f]
+    | NormalVariable v
+    deriving (Eq, Ord, Data, Typeable)
+
+instance (Constants p, Eq (NormalSentence v p f)) => Constants (NormalSentence v p f) where
+    fromBool x = NFPredicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance Negatable (NormalSentence v p f) where
+    negatePrivate = NFNot
+    foldNegation normal inverted (NFNot x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+{-
+instance (Arity p, Constants p, Combinable (NormalSentence v p f)) => Pred p (NormalTerm v f) (NormalSentence v p f) where
+    pApp0 x = NFPredicate x []
+    pApp1 x a = NFPredicate x [a]
+    pApp2 x a b = NFPredicate x [a,b]
+    pApp3 x a b c = NFPredicate x [a,b,c]
+    pApp4 x a b c d = NFPredicate x [a,b,c,d]
+    pApp5 x a b c d e = NFPredicate x [a,b,c,d,e]
+    pApp6 x a b c d e f = NFPredicate x [a,b,c,d,e,f]
+    pApp7 x a b c d e f g = NFPredicate x [a,b,c,d,e,f,g]
+    x .=. y = NFEqual x y
+    x .!=. y = NFNot (NFEqual x y)
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Variable v, Predicate p, Function f v, Combinable (NormalSentence v p f)) => Pretty (NormalSentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Predicate p, Function f v, Combinable (NormalSentence v p f)) => Formula (NormalSentence v p f) (NormalSentence v p f) where
+    atomic x@(NFPredicate _ _) = x
+    atomic x@(NFEqual _ _) = x
+    atomic _ = error "Chiou: atomic"
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f), Combinable (NormalSentence v p f), Term (NormalTerm v f) v f,
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (NormalSentence v p f) (NormalSentence v p f) v where
+    for_all _ _ = error "FirstOrderFormula NormalSentence"
+    exists _ _ = error "FirstOrderFormula NormalSentence"
+    foldFirstOrder _ co tf at f =
+        case f of
+          NFNot x -> co ((:~:) x)
+          NFEqual _ _ -> at f
+          NFPredicate p _ -> maybe (at f) tf (asBool p)
+{-
+    zipFirstOrder _ co tf at f1 f2 =
+        case (f1, f2) of
+          (NFNot f1', NFNot f2') -> co ((:~:) f1') ((:~:) f2')
+          (NFEqual _ _, NFEqual _ _) -> at f1 f2
+          (NFPredicate _ _, NFPredicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Combinable (NormalSentence v p f), Predicate p, Function f v, Variable v) => HasFixity (NormalSentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Variable v, Function f v) => Term (NormalTerm v f) v f where
+    vt = NormalVariable
+    fApp = NormalFunction
+    foldTerm v f t =
+            case t of
+              NormalVariable x -> v x
+              NormalFunction x ts -> f x ts
+    zipTerms v fn t1 t2 =
+        case (t1, t2) of
+          (NormalVariable x1, NormalVariable x2) -> v x1 x2
+          (NormalFunction f1 ts1, NormalFunction f2 ts2) -> fn f1 ts1 f2 ts2
+          _ -> Nothing
+
+toSentence :: (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Atom (Sentence v p f) (CTerm v f) v, Function f v, Variable v, Predicate p) =>
+              NormalSentence v p f -> Sentence v p f
+toSentence (NFNot s) = (.~.) (toSentence s)
+toSentence (NFEqual t1 t2) = toTerm t1 .=. toTerm t2
+toSentence (NFPredicate p ts) = pApp p (map toTerm ts)
+
+toTerm :: (Variable v, Function f v) => NormalTerm v f -> CTerm v f
+toTerm (NormalFunction f ts) = fApp f (map toTerm ts)
+toTerm (NormalVariable v) = vt v
+
+fromSentence :: forall v p f. (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Predicate p) =>
+                Sentence v p f -> NormalSentence v p f
+fromSentence = foldFirstOrder 
+                 (\ _ _ _ -> error "fromSentence 1")
+                 (\ cm ->
+                      case cm of
+                        ((:~:) f) -> NFNot (fromSentence f)
+                        _ -> error "fromSentence 2")
+                 (\ x -> NFPredicate (fromBool x) [])
+                 (foldAtomEq (\ p ts -> NFPredicate p (map fromTerm ts))
+                             (\ x -> NFPredicate (fromBool x) [])
+                             (\ t1 t2 -> NFEqual (fromTerm t1) (fromTerm t2)))
+
+fromTerm :: CTerm v f -> NormalTerm v f
+fromTerm (Function f ts) = NormalFunction f (map fromTerm ts)
+fromTerm (Variable v) = NormalVariable v
diff --git a/testdata/split-expected/Data/Logic/Instances/PropLogic.hs b/testdata/split-expected/Data/Logic/Instances/PropLogic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Instances/PropLogic.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.PropLogic
+    ( flatten
+    , plSat0
+    , plSat
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (clauseNormalForm', fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set.Extra as S (Set, toList)
+import PropLogic (PropAlg(satisfiable), PropForm(..))
+
+instance Negatable (PropForm a) where
+    negatePrivate = N
+    foldNegation normal inverted (N x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance {- Ord a => -} Combinable (PropForm a) where
+    x .<=>. y = EJ [x, y]
+    x .=>.  y = SJ [x, y]
+    x .|.   y = DJ [x, y]
+    x .&.   y = CJ [x, y]
+
+instance (Pretty a, HasFixity a, Ord a) => Formula (PropForm a) a where
+    atomic = A
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (PropForm a), Pretty a, HasFixity a, Ord a) => PropositionalFormula (PropForm a) a where
+    foldPropositional co tf at formula =
+        case formula of
+          -- EJ [x,y,z,...] -> CJ [EJ [x,y], EJ[y,z], ...]
+          EJ [] -> error "Empty equijunct"
+          EJ [x] -> foldPropositional co tf at x
+          EJ [x0, x1] -> co (BinOp x0 (:<=>:) x1)
+          EJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> EJ [x0, x1]) (pairs xs)))
+          SJ [] -> error "Empty subjunct"
+          SJ [x] -> foldPropositional co tf at x
+          SJ [x0, x1] -> co (BinOp x0 (:=>:) x1)
+          SJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> SJ [x0, x1]) (pairs xs)))
+          DJ [] -> tf False
+          DJ [x] -> foldPropositional co tf at x
+          DJ (x0:xs) -> co (BinOp x0 (:|:) (DJ xs))
+          CJ [] -> tf True
+          CJ [x] -> foldPropositional co tf at x
+          CJ (x0:xs) -> co (BinOp x0 (:&:) (CJ xs))
+          N x -> co ((:~:) x)
+          -- Not sure what to do about these - so far not an issue.
+          T -> tf True
+          F -> tf False
+          A x -> at x
+
+instance Constants (PropForm formula) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (PropositionalFormula (PropForm atom) atom, Pretty atom, HasFixity atom) => Pretty (PropForm atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (PropositionalFormula (PropForm atom) atom, HasFixity atom) => HasFixity (PropForm atom) where
+    fixity = fixityPropositional
+
+pairs :: [a] -> [(a, a)]
+pairs (x:y:zs) = (x,y) : pairs (y:zs)
+pairs _ = []
+
+flatten :: PropForm a -> PropForm a
+flatten (CJ xs) =
+    CJ (concatMap f (map flatten xs))
+    where
+      f (CJ ys) = ys
+      f x = [x]
+flatten (DJ xs) =
+    DJ (concatMap f (map flatten xs))
+    where
+      f (DJ ys) = ys
+      f x = [x]
+flatten (EJ xs) = EJ (map flatten xs)
+flatten (SJ xs) = SJ (map flatten xs)
+flatten (N x) = N (flatten x)
+flatten x = x
+
+plSat0 :: (PropAlg a (PropForm formula), PropositionalFormula formula atom, Ord formula) => PropForm formula -> Bool
+plSat0 f = satisfiable . (\ (x :: PropForm formula) -> x) . clauses0 $ f
+
+clauses0 :: (PropositionalFormula formula atom, Ord formula) => PropForm formula -> PropForm formula
+clauses0 f = CJ . map DJ . map S.toList . S.toList $ clauseNormalForm' f
+
+plSat :: forall m formula atom term v f. (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+                formula -> SkolemT v term m Bool
+plSat f = clauses f >>= (\ (x :: PropForm formula) -> return x) >>= return . satisfiable
+
+clauses :: forall m formula atom term v f.
+           (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+           formula -> SkolemT v term m (PropForm formula)
+clauses f =
+    do (cnf :: S.Set (S.Set formula)) <- clauseNormalForm f
+       return . CJ . map DJ . map (map A) . map S.toList . S.toList $ cnf
diff --git a/testdata/split-expected/Data/Logic/Instances/SatSolver.hs b/testdata/split-expected/Data/Logic/Instances/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Instances/SatSolver.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.SatSolver where
+
+import Control.Monad.State (get, put)
+import Control.Monad.Trans (lift)
+import Data.Boolean.SatSolver (assertTrue', CNF, Literal(..), newSatSolver, solve)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.ClauseNormalForm (ClauseNormalFormula(..))
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import qualified Data.Logic.Classes.Literal.Literal as N (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable(..), negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import Data.Logic.Normal.Implicative (LiteralMapT, NormalT)
+import qualified Data.Map as M (insert, lookup)
+import qualified Data.Set.Extra as S (fromList, ssMapM, toList)
+
+instance Ord Literal where
+    compare (Neg _) (Pos _) = LT
+    compare (Pos _) (Neg _) = GT
+    compare (Pos m) (Pos n) = compare m n
+    compare (Neg m) (Neg n) = compare m n
+
+instance Negatable Literal where
+    negatePrivate (Neg x) = Pos x
+    negatePrivate (Pos x) = Neg x
+    foldNegation _ inverted (Neg x) = inverted (Pos x)
+    foldNegation normal _ (Pos x) = normal (Pos x)
+
+deriving instance Data Literal
+deriving instance Typeable Literal
+
+instance ClauseNormalFormula CNF Literal where
+    clauses = S.fromList . map S.fromList
+    makeCNF = map S.toList . S.toList
+    satisfiable cnf = return . not . null $ assertTrue' cnf newSatSolver >>= solve
+
+toCNF :: (Monad m,
+          FirstOrderFormula formula atom v,
+          PropositionalFormula formula atom,
+          Atom atom term v,
+          AtomEq atom p term,
+          Term term v f,
+          N.Literal formula atom,
+          Ord formula) =>
+         formula -> NormalT formula v term m CNF
+toCNF f = clauseNormalForm f >>= S.ssMapM (lift . toLiteral) >>= return . makeCNF
+
+-- |Convert a [[formula]] to CNF, which means building a map from
+-- formula to Literal.
+toLiteral :: forall m lit. (Monad m, Negatable lit, Ord lit) =>
+             lit -> LiteralMapT lit m Literal
+toLiteral f =
+    literalNumber >>= return . if negated f then Neg else Pos
+    where
+      literalNumber :: LiteralMapT lit m Int
+      literalNumber =
+          get >>= \ (count, m) ->
+          case M.lookup f' m of
+            Nothing -> do let m' = M.insert f' count m
+                          put (count+1, m') 
+                          return count
+            Just n -> return n
+      f' :: lit
+      f' = if negated f then (.~.) f else f
diff --git a/testdata/split-expected/Data/Logic/KnowledgeBase.hs b/testdata/split-expected/Data/Logic/KnowledgeBase.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/KnowledgeBase.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, PackageImports,
+             RankNTypes, TemplateHaskell, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall #-}
+
+{- KnowledgeBase.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.KnowledgeBase
+    ( WithId(WithId, wiItem, wiIdent) -- Probably only used by some unit tests, and not really correctly
+    , ProverT
+    , runProver'
+    , runProverT'
+    , getKB
+    , unloadKB
+    -- , deleteKB
+    , askKB
+    , theoremKB
+    , inconsistantKB
+    , ProofResult(Proved, Disproved, Invalid)
+    , Proof(Proof, proofResult, proof)
+    , validKB
+    , tellKB
+    , loadKB
+    , showKB
+    ) where
+
+import "mtl" Control.Monad.Identity (Identity(runIdentity))
+import "mtl" Control.Monad.State (evalStateT, MonadState(get, put), StateT)
+import "mtl" Control.Monad.Trans (lift)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm)
+import Data.Logic.Resolution (getSetOfSupport, prove, SetOfSupport)
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as S (empty, map, minView, null, partition, Set, union)
+import Prelude hiding (negate)
+
+type SentenceCount = Int
+
+data WithId a = WithId {wiItem :: a, wiIdent :: Int} deriving (Eq, Ord, Show, Data, Typeable)
+
+withId :: Int -> a -> WithId a
+withId i x = WithId {wiIdent = i, wiItem = x}
+
+{-
+withIdPairs :: [WithId a] -> [(a, Int)]
+withIdPairs = map (\ x -> (wiItem x, wiIdent x))
+
+wiLookupId :: Eq a => a -> [WithId a] -> Maybe Int
+wiLookupId x xs = lookup x (withIdPairs xs)
+
+withIdPairs' :: [WithId a] -> [(Int, a)]
+withIdPairs' = map (\ x -> (wiIdent x, wiItem x))
+
+wiLookupItem :: Int -> [WithId a] -> Maybe a
+wiLookupItem i xs = lookup i (withIdPairs' xs)
+-}
+
+type KnowledgeBase inf = S.Set (WithId inf)
+
+data ProverState inf
+    = ProverState
+      { recursionLimit :: Maybe Int
+      , knowledgeBase :: KnowledgeBase inf
+      , sentenceCount :: Int }
+
+zeroKB :: Maybe Int -> ProverState inf
+zeroKB limit =
+    ProverState
+         { recursionLimit = limit
+         , knowledgeBase = S.empty
+         , sentenceCount = 1 }
+
+-- |A monad for running the knowledge base.
+type ProverT inf = StateT (ProverState inf)
+type ProverT' v term inf m a = ProverT inf (SkolemT v term m) a
+
+runProverT' :: Monad m => Maybe Int -> ProverT' v term inf m a -> m a
+runProverT' limit = runSkolemT . runProverT limit
+runProverT :: Monad m => Maybe Int -> StateT (ProverState inf) m a -> m a
+runProverT limit action = evalStateT action (zeroKB limit)
+runProver' :: Maybe Int -> ProverT' v term inf Identity a -> a
+runProver' limit = runIdentity . runProverT' limit
+{-
+runProver :: StateT (ProverState inf) Identity a -> a
+runProver = runIdentity . runProverT
+-}
+
+data ProofResult
+    = Disproved
+    -- ^ The conjecture is unsatisfiable
+    | Proved
+    -- ^ The negated conjecture is unsatisfiable
+    | Invalid
+    -- ^ Both are satisfiable
+    deriving (Data, Typeable, Eq, Ord, Show)
+
+$(deriveSafeCopy 1 'base ''ProofResult)
+
+data Proof lit = Proof {proofResult :: ProofResult, proof :: S.Set (ImplicativeForm lit)} deriving (Data, Typeable, Eq, Ord)
+
+instance (Ord lit, Show lit, Literal lit atom, FirstOrderFormula lit atom v) => Show (Proof lit) where
+    show p = "Proof {proofResult = " ++ show (proofResult p) ++ ", proof = " ++ show (proof p) ++ "}"
+
+-- |Remove a particular sentence from the knowledge base
+unloadKB :: (Monad m, Ord inf) => SentenceCount -> ProverT inf m (Maybe (KnowledgeBase inf))
+unloadKB n =
+    do st <- get
+       let (discard, keep) = S.partition ((== n) . wiIdent) (knowledgeBase st)
+       put (st {knowledgeBase = keep}) >> return (Just discard)
+
+-- |Return the contents of the knowledgebase.
+getKB :: Monad m => ProverT inf m (S.Set (WithId inf))
+getKB = get >>= return . knowledgeBase
+
+-- |Return a flag indicating whether sentence was disproved, along
+-- with a disproof.
+inconsistantKB :: forall m formula atom term v p f lit.
+                  (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+                   Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+                  formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+inconsistantKB s =
+    get >>= \ st ->
+    lift (implicativeNormalForm s) >>=
+    return . getSetOfSupport >>= \ sos ->
+    getKB >>=
+    return . prove (recursionLimit st) S.empty sos . S.map wiItem
+
+-- |Return a flag indicating whether sentence was proved, along with a
+-- proof.
+theoremKB :: forall m formula atom term v p f lit.
+             (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+              Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+             formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+theoremKB s = inconsistantKB ((.~.) s)
+
+-- |Try to prove a sentence, return the result and the proof.
+-- askKB should be in KnowledgeBase module. However, since resolution
+-- is here functions are here, it is also placed in this module.
+askKB :: (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+          Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+         formula -> ProverT' v term (ImplicativeForm lit) m Bool
+askKB s = theoremKB s >>= return . fst
+
+-- |See whether the sentence is true, false or invalid.  Return proofs
+-- for truth and falsity.
+validKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+            Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+           formula -> ProverT' v term (ImplicativeForm lit) m (ProofResult, SetOfSupport lit v term, SetOfSupport lit v term)
+validKB s =
+    theoremKB s >>= \ (proved, proof1) ->
+    inconsistantKB s >>= \ (disproved, proof2) ->
+    return (if proved then Proved else if disproved then Disproved else Invalid, proof1, proof2)
+
+-- |Validate a sentence and insert it into the knowledgebase.  Returns
+-- the INF sentences derived from the new sentence, or Nothing if the
+-- new sentence is inconsistant with the current knowledgebase.
+tellKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+          formula -> ProverT' v term (ImplicativeForm lit) m (Proof lit)
+tellKB s =
+    do st <- get
+       inf <- lift (implicativeNormalForm s)
+       let inf' = S.map (withId (sentenceCount st)) inf
+       (valid, _, _) <- validKB s
+       case valid of
+         Disproved -> return ()
+         _ -> put st { knowledgeBase = S.union (knowledgeBase st) inf'
+                     , sentenceCount = sentenceCount st + 1 }
+       return $ Proof {proofResult = valid, proof = S.map wiItem inf'}
+
+loadKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+          [formula] -> ProverT' v term (ImplicativeForm lit) m [Proof lit]
+loadKB sentences = mapM tellKB sentences
+
+-- |Delete an entry from the KB.
+{-
+deleteKB :: Monad m => Int -> ProverT inf m String
+deleteKB i = do st <- get
+                modify (\ st' -> st' {knowledgeBase = deleteElement i (knowledgeBase st')})
+                st' <- get
+		return (if length (knowledgeBase st') /= length (knowledgeBase st) then
+			  "Deleted"
+			else
+			  "Failed to delete")
+	     
+deleteElement :: Int -> [a] -> [a]
+deleteElement i l
+    | i <= 0    = l
+    | otherwise = let
+		    (p1, p2) = splitAt (i - 1) l
+		  in
+		    p1 ++ (case p2 of
+			       [] -> []
+			       _ -> tail p2)
+-}
+
+-- |Return a text description of the contents of the knowledgebase.
+showKB :: (Show inf, Monad m) => ProverT inf m String
+showKB = get >>= return . reportKB
+
+reportKB :: (Show inf) => ProverState inf -> String
+reportKB st@(ProverState {knowledgeBase = kb}) =
+    case S.minView kb of
+      Nothing -> "Nothing in Knowledge Base\n"
+      Just (WithId {wiItem = x, wiIdent = n}, kb')
+          | S.null kb' ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n"
+          | True ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n" ++ reportKB (st {knowledgeBase = kb'})
diff --git a/testdata/split-expected/Data/Logic/Normal/Clause.hs b/testdata/split-expected/Data/Logic/Normal/Clause.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Normal/Clause.hs
@@ -0,0 +1,94 @@
+-- |A series of transformations to convert first order logic formulas
+-- into (ultimately) Clause Normal Form.
+-- 
+-- @
+-- 1st order formula:
+--   ∀Y (∀X (taller(Y,X) | wise(X)) => wise(Y))
+-- 
+-- Simplify
+--   ∀Y (~∀X (taller(Y,X) | wise(X)) | wise(Y))
+-- 
+-- Move negations in - Negation Normal Form
+--   ∀Y (∃X (~taller(Y,X) & ~wise(X)) | wise(Y))
+-- 
+-- Move quantifiers out - Prenex Normal Form
+--   ∀Y (∃X ((~taller(Y,X) & ~wise(X)) | wise(Y)))
+-- 
+-- Distribute disjunctions
+--   ∀Y ∃X ((~taller(Y,X) | wise(Y)) & (~wise(X) | wise(Y)))
+-- 
+-- Skolemize  - Skolem Normal Form
+--   ∀Y (~taller(Y,x(Y)) | wise(Y)) & (~wise(x(Y)) | wise(Y))
+-- 
+-- Convert to CNF
+--   { ~taller(Y,x(Y)) | wise(Y),
+--     ~wise(x(Y)) | wise(Y) } 
+-- @
+-- 
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Clause
+    ( clauseNormalForm
+    , cnfTrace
+    ) where
+
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq, prettyAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder)
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Literal.PrettyLit (prettyLit)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Normal (simpcnf')
+import Data.Logic.Harrison.Skolem (nnf, pnf, simplify, skolemize, SkolemT)
+import qualified Data.Set.Extra as Set (Set, toList)
+import Text.PrettyPrint (($$), brackets, Doc, hcat, nest, render, text, vcat)
+
+-- |Convert to Skolem Normal Form and then distribute the disjunctions over the conjunctions:
+-- 
+-- @
+-- Formula      Rewrites to
+-- P | (Q & R)  (P | Q) & (P | R)
+-- (Q & R) | P  (Q | P) & (R | P)
+-- @
+-- 
+clauseNormalForm :: forall formula atom term v f lit m.
+                    (Monad m,
+                     FirstOrderFormula formula atom v,
+                     PropositionalFormula formula atom,
+                     Atom atom term v,
+                     Term term v f,
+                     Literal lit atom,
+                     Ord formula, Ord lit) =>
+                    formula -> SkolemT v term m (Set.Set (Set.Set lit))
+clauseNormalForm fm = skolemize id fm >>= return . (simpcnf' :: formula -> Set.Set (Set.Set lit))
+
+cnfTrace :: forall m formula atom term v p f lit.
+            (Monad m,
+             FirstOrderFormula formula atom v,
+             PropositionalFormula formula atom,
+             Atom atom term v,
+             AtomEq atom p term,
+             Term term v f,
+             Literal lit atom,
+             Ord formula, Ord lit) =>
+            (v -> Doc)
+         -> (p -> Doc)
+         -> (f -> Doc)
+         -> formula
+         -> SkolemT v term m (String, Set.Set (Set.Set lit))
+cnfTrace pv pp pf f =
+    do (snf :: formula) <- skolemize id f
+       cnf <- clauseNormalForm f
+       return (render (vcat
+                       [text "Original:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 f),
+                        text "Simplified:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (simplify f)),
+                        text "Negation Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (nnf . simplify $ f)),
+                        text "Prenex Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (pnf f)),
+                        text "Skolem Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 snf),
+                        text "Clause Normal Form:" $$ vcat (map prettyClause (fromSS cnf))]), cnf)
+    where
+      prettyClause (clause :: [lit]) =
+          nest 2 . brackets . hcat . intersperse (text ", ") . map (nest 2 . brackets . prettyLit (prettyAtomEq pv pp pf) pv 0) $ clause
+      fromSS = (map Set.toList) . Set.toList 
diff --git a/testdata/split-expected/Data/Logic/Normal/Implicative.hs b/testdata/split-expected/Data/Logic/Normal/Implicative.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Normal/Implicative.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE DeriveDataTypeable, PackageImports, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Implicative
+    ( LiteralMapT
+    , NormalT
+    , runNormal
+    , runNormalT
+    , ImplicativeForm(INF, neg, pos)
+    , makeINF'
+    , implicativeNormalForm
+    , prettyINF
+    , prettyProof
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (MonadPlus, msum, StateT(runStateT))
+import Data.Generics (Data, listify, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (ifElse, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Skolem (Skolem(isSkolem))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (empty, flatten, fold, fromList, insert, map, Set, singleton, toList)
+import Text.PrettyPrint (cat, Doc, hsep, text)
+
+-- |Combination of Normal monad and LiteralMap monad
+type NormalT formula v term m a = SkolemT v term (LiteralMapT formula m) a
+
+runNormalT :: Monad m => NormalT formula v term m a -> m a
+runNormalT action = runLiteralMapM (runSkolemT action)
+
+runNormal :: NormalT formula v term Identity a -> a
+runNormal = runIdentity . runNormalT
+ 
+--type LiteralMap f = LiteralMapT f Identity
+type LiteralMapT f = StateT (Int, Map.Map f Int)
+
+--runLiteralMap :: LiteralMap p a -> a
+--runLiteralMap action = runIdentity (runLiteralMapM action)
+
+runLiteralMapM :: Monad m => LiteralMapT f m a -> m a
+runLiteralMapM action = (runStateT action) (1, Map.empty) >>= return . fst
+
+-- |A type to represent a formula in Implicative Normal Form.  Such a
+-- formula has the form @a & b & c .=>. d | e | f@, where a thru f are
+-- literals.  One more restriction that is not implied by the type is
+-- that no literal can appear in both the pos set and the neg set.
+data ImplicativeForm lit =
+    INF {neg :: Set.Set lit, pos :: Set.Set lit}
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+-- |A version of MakeINF that takes lists instead of sets, used for
+-- implementing a more attractive show method.
+makeINF' :: (Negatable lit, Ord lit) => [lit] -> [lit] -> ImplicativeForm lit
+makeINF' n p = INF (Set.fromList n) (Set.fromList p)
+
+prettyINF :: (Negatable lit, Ord lit) => (lit -> Doc) -> ImplicativeForm lit -> Doc
+prettyINF lit x = cat $ [text "(", hsep (map lit (Set.toList (neg x))),
+                         text ") => (", hsep (map lit (Set.toList (pos x))), text ")"]
+
+prettyProof :: (Negatable lit, Ord lit) => (lit -> Doc) -> Set.Set (ImplicativeForm lit) -> Doc
+prettyProof lit p = cat $ [text "["] ++ intersperse (text ", ") (map (prettyINF lit) (Set.toList p)) ++ [text "]"]
+
+-- |Take the clause normal form, and turn it into implicative form,
+-- where each clauses becomes an (LHS, RHS) pair with the negated
+-- literals on the LHS and the non-negated literals on the RHS:
+-- @
+--   (a | ~b | c | ~d) becomes (b & d) => (a | c)
+--   (~b | ~d) | (a | c)
+--   ~~(~b | ~d) | (a | c)
+--   ~(b & d) | (a | c)
+-- @
+-- If there are skolem functions on the RHS, split the formula using
+-- this identity:
+-- @
+--   (a | b | c) => (d & e & f)
+-- @
+-- becomes
+-- @
+--    a | b | c => d
+--    a | b | c => e
+--    a | b | c => f
+-- @
+implicativeNormalForm :: forall m formula atom term v f lit. 
+                         (Monad m,
+                          FirstOrderFormula formula atom v,
+                          PropositionalFormula formula atom,
+                          Atom atom term v,
+                          Literal lit atom,
+                          Term term v f,
+                          Data formula, Ord formula, Ord lit, Data lit, Skolem f v) =>
+                         formula -> SkolemT v term m (Set.Set (ImplicativeForm lit))
+implicativeNormalForm formula =
+    do cnf <- clauseNormalForm formula
+       let pairs = Set.map (Set.fold collect (Set.empty, Set.empty)) cnf :: Set.Set (Set.Set lit, Set.Set lit)
+           pairs' = Set.flatten (Set.map split pairs) :: Set.Set (Set.Set lit, Set.Set lit)
+       return (Set.map (\ (n,p) -> INF n p) pairs')
+    where
+      collect :: lit -> (Set.Set lit, Set.Set lit) -> (Set.Set lit, Set.Set lit)
+      collect f (n, p) =
+          foldLiteral (\ f' -> (Set.insert f' n, p))
+                      (ifElse (n, Set.insert true p) (Set.insert true n, p))
+                      (\ _ -> (n, Set.insert f p))
+                      f
+      split :: (Set.Set lit, Set.Set lit) -> Set.Set (Set.Set lit, Set.Set lit)
+      split (lhs, rhs) =
+          if any isSkolem (gFind rhs :: [f])
+          then Set.map (\ x -> (lhs, Set.singleton x)) rhs
+          else Set.singleton (lhs, rhs)
+
+-- | @gFind a@ will extract any elements of type @b@ from
+-- @a@'s structure in accordance with the MonadPlus
+-- instance, e.g. Maybe Foo will return the first Foo
+-- found while [Foo] will return the list of Foos found.
+gFind :: (MonadPlus m, Data a, Typeable b) => a -> m b
+gFind = msum . map return . listify (const True)
diff --git a/testdata/split-expected/Data/Logic/Resolution.hs b/testdata/split-expected/Data/Logic/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Resolution.hs
@@ -0,0 +1,374 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS -Wall -Wwarn #-}
+
+{- Resolution.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.Resolution
+    ( prove
+    , getSetOfSupport
+    , SetOfSupport
+    , Unification
+    , isRenameOfAtomEq
+    , getSubstAtomEq
+    ) where
+
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Atom (Atom(isRename, getSubst))
+import Data.Logic.Classes.Constants (fromBool)
+import Data.Logic.Classes.Equals (applyEq, AtomEq(foldAtomEq, equals), zipAtomsEq)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Literal.ZipLiterals (zipLiterals)
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Normal.Implicative (ImplicativeForm(INF, neg, pos))
+import Data.Map (empty, Map)
+import qualified Data.Map as Map (insert, insertWith, intersection, lookup, Map, union, unionWith)
+import Data.Maybe (isJust)
+import qualified Data.Set.Extra as S (any, catMaybes, deleteFindMin, empty, insert, map, minView, null, Set, singleton, size, toList, union, unions)
+
+type SetOfSupport lit v term = S.Set (Unification lit v term)
+
+type Unification lit v term = (ImplicativeForm lit, Map.Map v term)
+
+prove :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, {-Show v, Show term,-} AtomEq atom p term, Predicate p) =>
+         Maybe Int -- ^ Recursion limit.  We continue recursing until this
+                   -- becomes zero.  If it is negative it may recurse until
+                   -- it overflows the stack.
+      -> SetOfSupport lit v term
+      -> SetOfSupport lit v term
+      -> S.Set (ImplicativeForm lit)
+      -> (Bool, SetOfSupport lit v term)
+prove (Just 0) ss1 _ _ = (False, ss1)
+prove limit ss1 ss2' kb  =
+    case S.minView ss2' of
+      Nothing -> (False, ss1)
+      Just (s, ss2) ->
+          case prove' s kb ss2 ss1 of
+            (ss', True) -> (True, (S.insert s (S.union ss1 ss')))
+            (ss', False) -> prove (maybe Nothing (\ n -> Just (n - 1)) limit) (S.insert s ss1) ss' (S.insert (fst s) kb)
+-- prove ss1 [] _kb = (False, ss1)
+-- prove ss1 (s:ss2) kb =
+--     let
+--       (ss', tf) = prove' s kb ss2 ss1
+--     in
+--       if tf then
+--         (True, (ss1 ++ [s] ++ss'))
+--       else
+--         prove (ss1 ++ [s]) ss' (fst s:kb)
+
+prove' :: forall lit atom p f v term.
+          (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+          Unification lit v term -> S.Set (ImplicativeForm lit) -> SetOfSupport lit v term -> SetOfSupport lit v term -> (SetOfSupport lit v term, Bool)
+prove' p kb ss1 ss2 =
+    let
+      res1 = S.map (\x -> resolution p (x, empty)) kb
+      res2 = S.map (\x -> resolution (x, empty) p) kb
+      dem1 = S.map (\e -> demodulate p (e, empty)) kb
+      dem2 = S.map (\p' -> demodulate (p', empty) p) kb
+      (ss', tf) = getResult (S.union ss1 ss2) (S.unions [res1, res2, dem1, dem2])
+    in
+      if S.null ss' then (ss1, False) else (S.union ss1 ss', tf)
+
+getResult :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+             SetOfSupport lit v term -> S.Set (Maybe (Unification lit v term)) -> ((SetOfSupport lit v term), Bool)
+getResult ss us =
+    case S.minView us of
+      Nothing ->
+          (S.empty, False)
+      Just (Nothing, xs) ->
+          getResult ss xs
+      Just ((Just x@(inf, _v)), xs) ->
+          if S.null (neg inf) && S.null (pos inf)
+          then (S.singleton x, True)
+          else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+               then getResult ss xs
+               else let (xs', tf) = getResult ss xs in (S.insert x xs', tf)
+{-
+getResult _ [] = (S.empty, False)
+getResult ss (Nothing:xs) = getResult ss xs
+getResult ss ((Just x):xs)  =
+    if S.null (neg inf) && S.null (pos inf)
+    then (S.singleton x, True)
+    else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+         then getResult ss xs
+         else let (xs', tf) = getResult ss xs in (S.insert x xs' tf)
+    where
+      (inf, _v) = x
+-}
+
+-- |Convert the "question" to a set of support.
+getSetOfSupport :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v) =>
+                   S.Set (ImplicativeForm lit) -> S.Set (ImplicativeForm lit, Map.Map v term)
+getSetOfSupport s = S.map (\ x -> (x, getSubsts x empty)) s
+
+getSubsts :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+             ImplicativeForm lit -> Map.Map v term -> Map.Map v term
+getSubsts inf theta =
+    getSubstSentences (pos inf) (getSubstSentences (neg inf) theta)
+
+getSubstSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> Map.Map v term -> Map.Map v term
+getSubstSentences xs theta = foldr getSubstSentence theta (S.toList xs)
+
+
+getSubstSentence :: (Literal lit atom, Atom atom term v, Term term v f)  => lit -> Map.Map v term -> Map.Map v term
+getSubstSentence formula theta =
+    foldLiteral
+          (\ s -> getSubstSentence s theta)
+          (const theta)
+          (getSubst theta)
+          formula
+
+getSubstAtomEq :: forall atom p term v f. (AtomEq atom p term, Term term v f) => Map v term -> atom -> Map v term
+getSubstAtomEq theta = foldAtomEq (\ _ ts -> getSubstsTerms ts theta) (const theta) (\ t1 t2 -> getSubstsTerms [t1, t2] theta)
+
+getSubstsTerms :: Term term v f => [term] -> Map.Map v term -> Map.Map v term
+getSubstsTerms [] theta = theta
+getSubstsTerms (x:xs) theta =
+    let
+      theta' = getSubstsTerm x theta
+      theta'' = getSubstsTerms xs theta'
+    in
+      theta''
+
+getSubstsTerm :: Term term v f => term -> Map.Map v term -> Map.Map v term
+getSubstsTerm term theta =
+    foldTerm (\ v -> Map.insertWith (\ _ old -> old) v (vt v) theta)
+             (\ _ ts -> getSubstsTerms ts theta)
+             term
+
+isRenameOf :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+              ImplicativeForm lit -> ImplicativeForm lit -> Bool
+isRenameOf inf1 inf2 =
+    (isRenameOfSentences lhs1 lhs2) && (isRenameOfSentences rhs1 rhs2)
+    where
+      lhs1 = neg inf1
+      rhs1 = pos inf1
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+isRenameOfSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> S.Set lit -> Bool
+isRenameOfSentences xs1 xs2 =
+    S.size xs1 == S.size xs2 && all (uncurry isRenameOfSentence) (zip (S.toList xs1) (S.toList xs2))
+
+isRenameOfSentence :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => lit -> lit -> Bool
+isRenameOfSentence f1 f2 =
+    maybe False id $
+    zipLiterals (\ _ _ -> Just False) (\ x y -> Just (x == y)) (\ x y -> Just (isRename x y)) f1 f2
+
+isRenameOfAtomEq :: (AtomEq atom p term, Term term v f) => atom -> atom -> Bool
+isRenameOfAtomEq a1 a2 =
+    maybe False id $
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> Just (p1 == p2 && isRenameOfTerms ts1 ts2))
+               (\ x y -> Just (x == y))
+               (\ t1l t1r t2l t2r -> Just (isRenameOfTerm t1l t2l && isRenameOfTerm t1r t2r))
+               a1 a2
+
+isRenameOfTerm :: Term term v f => term -> term -> Bool
+isRenameOfTerm t1 t2 =
+    maybe False id $
+    zipTerms (\ _ _ -> Just True)
+             (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && isRenameOfTerms ts1 ts2))
+             t1 t2
+
+isRenameOfTerms :: Term term v f => [term] -> [term] -> Bool
+isRenameOfTerms ts1 ts2 =
+    if length ts1 == length ts2 then
+      let
+        tsTuples = zip ts1 ts2
+      in
+        foldl (&&) True (map (\(t1, t2) -> isRenameOfTerm t1 t2) tsTuples)
+    else
+      False
+
+resolution :: forall lit atom p f term v. (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term, Eq p) =>
+             (ImplicativeForm lit, Map.Map v term) -> (ImplicativeForm lit, Map.Map v term) -> Maybe (ImplicativeForm lit, Map v term)
+resolution (inf1, theta1) (inf2, theta2) =
+    let
+        lhs1 = neg inf1
+        rhs1 = pos inf1
+        lhs2 = neg inf2
+        rhs2 = pos inf2
+        unifyResult = tryUnify rhs1 lhs2
+    in
+      case unifyResult of
+        Just ((rhs1', theta1'), (lhs2', theta2')) ->
+            let
+              lhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') lhs1)
+                              (S.catMaybes $ S.map (\s -> subst s theta2') lhs2')
+              rhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') rhs1')
+                              (S.catMaybes $ S.map (\s -> subst s theta2') rhs2)
+              theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2')
+            in
+              Just (INF lhs'' rhs'', theta)
+        Nothing -> Nothing
+    where
+      tryUnify :: (Literal lit atom, Ord lit) =>
+                  S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify lhs rhs = tryUnify' lhs rhs S.empty
+                         
+      tryUnify' :: (Literal lit atom, Ord lit) =>
+                   S.Set lit -> S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify' lhss _ _ | S.null lhss = Nothing
+      tryUnify' lhss'' rhss lhss' =
+          let (lhs, lhss) = S.deleteFindMin lhss'' in
+          case tryUnify'' lhs rhss S.empty of
+            Nothing -> tryUnify' lhss rhss (S.insert lhs lhss')
+            Just (rhss', theta1', theta2') ->
+                Just ((S.union lhss' lhss, theta1'), (rhss', theta2'))
+
+      tryUnify'' :: (Literal lit atom, Ord lit) =>
+                    lit -> S.Set lit -> S.Set lit -> Maybe (S.Set lit, Map.Map v term, Map.Map v term)
+      tryUnify'' _x rhss _ | S.null rhss = Nothing
+      tryUnify'' x rhss'' rhss' =
+          let (rhs, rhss) = S.deleteFindMin rhss'' in
+          case unify x rhs of
+            Nothing -> tryUnify'' x rhss (S.insert rhs rhss')
+            Just (theta1', theta2') -> Just (S.union rhss' rhss, theta1', theta2')
+
+-- |Try to unify the second argument using the equality in the first.
+demodulate :: (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term) =>
+              (Unification lit v term) -> (Unification lit v term) -> Maybe (Unification lit v term)
+demodulate (inf1, theta1) (inf2, theta2) =
+    case (S.null (neg inf1), S.toList (pos inf1)) of
+      (True, [lit1]) ->
+          foldLiteral (\ _ -> error "demodulate") (\ _ -> Nothing) (foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) p) lit1
+      _ -> Nothing
+    where
+      p t1 t2 =
+          case findUnify t1 t2 (S.union lhs2 rhs2) of
+            Just ((t1', t2'), theta1', theta2') ->
+                let substNeg2 = S.catMaybes $ S.map (\x -> subst x theta2') lhs2
+                    substPos2 = S.catMaybes $ S.map (\x -> subst x theta2') rhs2
+                    lhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substNeg2
+                    rhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substPos2
+                    theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2') in
+                Just (INF lhs rhs, theta)
+            Nothing -> Nothing
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+-- |Unification: unifies two sentences.
+unify :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+         lit -> lit -> Maybe (Map.Map v term, Map.Map v term)
+unify s1 s2 = unify' s1 s2 empty empty
+
+unify' :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+          lit -> lit -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unify' f1 f2 theta1 theta2 =
+    zipLiterals
+         (\ _ _ -> error "unify'")
+         (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+         (unify2AtomsEq theta1 theta2)
+         f1 f2
+
+unify2AtomsEq :: (AtomEq atom p term, Term term v f) => Map.Map v term -> Map.Map v term -> atom -> atom -> Maybe (Map.Map v term, Map.Map v term)
+unify2AtomsEq theta1 theta2 a1 a2 =
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> if p1 == p2 then unifyTerms ts1 ts2 theta1 theta2 else Nothing)
+               (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+               (\ l1 r1 l2 r2 -> unifyTerms [l1, r1] [l2, r2] theta1 theta2)
+               a1 a2
+
+unifyTerm :: Term term v f => term -> term -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerm t1 t2 theta1 theta2 =
+    foldTerm
+          (\ v1 ->
+               maybe (if vt v1 == t2 then Nothing else Just (Map.insert v1 t2 theta1, theta2))
+                     (\ t1' -> unifyTerm t1' t2 theta1 theta2)
+                     (Map.lookup v1 theta1))
+          (\ f1 ts1 ->
+               foldTerm (\ v2 -> maybe (Just (theta1, Map.insert v2 t1 theta2))
+                                 (\ t2' -> unifyTerm t1 t2' theta1 theta2)
+                                 (Map.lookup v2 theta2))
+                        (\ f2 ts2 -> if f1 == f2
+                                     then unifyTerms ts1 ts2 theta1 theta2
+                                     else Nothing)
+                        t2)
+          t1
+
+unifyTerms :: Term term v f =>
+              [term] -> [term] -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerms [] [] theta1 theta2 = Just (theta1, theta2)
+unifyTerms (t1:ts1) (t2:ts2) theta1 theta2 =
+    case (unifyTerm t1 t2 theta1 theta2) of
+      Nothing                -> Nothing
+      Just (theta1',theta2') -> unifyTerms ts1 ts2 theta1' theta2'
+unifyTerms _ _ _ _ = Nothing
+
+findUnify :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term) =>
+             term -> term -> S.Set lit -> Maybe ((term, term), Map.Map v term, Map.Map v term)
+findUnify tl tr s =
+    let
+      terms = concatMap (foldLiteral (\ (_ :: lit) -> error "getTerms") (\ _ -> []) p) (S.toList s)
+      unifiedTerms' = map (\t -> unifyTerm tl t empty empty) terms
+      unifiedTerms = filter isJust unifiedTerms'
+    in
+     case unifiedTerms of
+       [] -> Nothing
+       (Just (theta1, theta2)):_ ->
+         Just ((substTerm tl theta1, substTerm tr theta1), theta1, theta2)
+       (Nothing:_) -> error "findUnify"
+    where
+      -- getTerms lit = foldLiteral (\ _ -> error "getTerms") p formula
+      p :: atom -> [term]
+      p = foldAtomEq (\ _ ts -> concatMap getTerms' ts) (const []) (\ t1 t2 -> getTerms' t1 ++ getTerms' t2)
+      getTerms' :: term -> [term]
+      getTerms' t = foldTerm (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+
+{-
+getTerms :: Literal formula atom v => formula -> [term]
+getTerms formula =
+    foldLiteral (\ _ -> error "getTerms") p formula
+    where
+      getTerms' t = foldT (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+      p (Equal t1 t2) = getTerms' t1 ++ getTerms' t2
+      p (Apply _ ts) = concatMap getTerms' ts
+-}
+
+replaceTerm :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, Eq term, AtomEq atom p term) => lit -> (term, term) -> Maybe lit
+replaceTerm formula (tl', tr') =
+    foldLiteral
+          (\ _ -> error "error in replaceTerm")
+          (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (map (\ t -> replaceTerm' t) ts))))
+                      (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+                      (\ t1 t2 -> 
+                           let t1' = replaceTerm' t1
+                               t2' = replaceTerm' t2 in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+    where
+      replaceTerm' t =
+          if termEq t tl'
+          then tr'
+          else foldTerm vt (\ f ts -> fApp f (map replaceTerm' ts)) t
+      termEq t1 t2 =
+          maybe False id (zipTerms (\ v1 v2 -> Just (v1 == v2)) (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && length ts1 == length ts2 && all (uncurry termEq) (zip ts1 ts2))) t1 t2)
+
+subst :: (Literal formula atom, AtomEq atom p term, Atom atom term v, Term term v f, Eq term) => formula -> Map.Map v term -> Maybe formula
+subst formula theta =
+    foldLiteral
+          (\ _ -> Just formula)
+          (\ x -> Just (fromBool x))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (substTerms ts theta))))
+                      (Just . fromBool)
+                      (\ t1 t2 ->
+                           let t1' = substTerm t1 theta
+                               t2' = substTerm t2 theta in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+
+substTerm :: Term term v f => term -> Map.Map v term -> term
+substTerm term theta =
+    foldTerm (\ v -> maybe term id (Map.lookup v theta))
+             (\ f ts -> fApp f (substTerms ts theta))
+             term
+
+substTerms :: Term term v f => [term] -> Map.Map v term -> [term]
+substTerms ts theta = map (\t -> substTerm t theta) ts
+
+updateSubst :: Term term v f => Map.Map v term -> Map.Map v term -> Map.Map v term
+updateSubst theta1 theta2 = Map.union theta1 (Map.intersection theta1 theta2)
+-- This is what was in the original code, which behaves slightly differently
+--updateSubst theta1 _ | Map.null theta1 = Map.empty
+--updateSubst theta1 theta2 = Map.unionWith (\ _ term2 -> term2) theta1 theta2
diff --git a/testdata/split-expected/Data/Logic/Satisfiable.hs b/testdata/split-expected/Data/Logic/Satisfiable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Satisfiable.hs
@@ -0,0 +1,53 @@
+-- |Do satisfiability computations on any FirstOrderFormula formula by
+-- converting it to a convenient instance of PropositionalFormula and
+-- using the satisfiable function from that instance.  Currently we
+-- use the satisfiable function from the PropLogic package, by the
+-- Bucephalus project.
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Satisfiable
+    ( satisfiable
+    , theorem
+    , inconsistant
+    , invalid
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), toPropositional)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Instances.PropLogic ()
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set as Set (Set, toList)
+import qualified PropLogic as PL (PropAlg(satisfiable), PropForm(A, CJ, DJ))
+
+-- |Is there any variable assignment that makes the formula true?
+-- satisfiable :: forall formula atom term v f m. (Monad m, FirstOrderFormula formula atom v, Formula atom term v, Term term v f, Ord formula, Literal formula atom v, Ord atom) =>
+--                 formula -> SkolemT v term m Bool
+satisfiable :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                Ord atom, Monad m, Eq formula, Ord formula) =>
+               formula -> SkolemT v term m Bool
+satisfiable f =
+    do (clauses :: Set.Set (Set.Set formula)) <- clauseNormalForm f
+       let f' = PL.CJ . map (PL.DJ . map (toPropositional PL.A)) . map Set.toList . Set.toList $ clauses
+       return . PL.satisfiable $ f'
+
+-- |Is the formula always false?  (Not satisfiable.)
+inconsistant :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                 Ord atom, Monad m, Eq formula, Ord formula) =>
+                formula -> SkolemT v term m Bool
+inconsistant f =  satisfiable f >>= return . not
+
+-- |Is the negation of the formula inconsistant?
+theorem :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+theorem f = inconsistant ((.~.) f)
+
+-- |A formula is invalid if it is neither a theorem nor inconsistent.
+invalid :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+invalid f = inconsistant f >>= \ fi -> theorem f >>= \ ft -> return (not (fi || ft))
diff --git a/testdata/split-expected/Data/Logic/Types/Common.hs b/testdata/split-expected/Data/Logic/Types/Common.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Common.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Types.Common where
+
+import Data.Logic.Classes.Variable (Variable(..))
+import qualified Data.Set as Set
+import Text.PrettyPrint (text)
+
+instance Variable String where
+    variant x vars = if Set.member x vars then variant (x ++ "'") vars else x
+    prefix p x = p ++ x
+    prettyVariable = text
+
+{-
+instance Variable String where
+    variant v vs =
+        if Set.member v vs then variant (next v) (Set.insert v vs) else v
+        where
+          next :: String -> String
+          next s =
+              case break (not . isDigit) (reverse s) of
+                (_, "") -> "x"
+                ("", nondigits) -> nondigits ++ "2"
+                (digits, nondigits) -> nondigits ++ show (1 + read (reverse digits) :: Int)
+-}
diff --git a/testdata/split-expected/Data/Logic/Types/FirstOrder.hs b/testdata/split-expected/Data/Logic/Types/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/FirstOrder.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             GeneralizedNewtypeDeriving, MultiParamTypeClasses, TemplateHaskell, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures -fno-warn-orphans #-}
+-- |Data types which are instances of the Logic type class for use
+-- when you just want to use the classes and you don't have a
+-- particular representation you need to use.
+module Data.Logic.Types.FirstOrder
+    ( Formula(..)
+    , PTerm(..)
+    , Predicate(..)
+    ) where
+
+import Data.Data (Data)
+import qualified Data.Logic.Classes.Apply as C (Predicate)
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import Data.Logic.Classes.Equals (AtomEq(..), prettyAtomEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder, Quant(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (botFixity, HasFixity(..), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function, Term(..))
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.SafeCopy (base, deriveSafeCopy, extension, MigrateFrom(..), SafeCopy)
+import Data.Typeable (Typeable)
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula v p f
+    = Predicate (Predicate p (PTerm v f))
+    | Combine (Combination (Formula v p f))
+    | Quant Quant v (Formula v p f)
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- |A temporary type used in the fold method to represent the
+-- combination of a predicate and its arguments.  This reduces the
+-- number of arguments to foldFirstOrder and makes it easier to manage the
+-- mapping of the different instances to the class methods.
+data Predicate p term
+    = Equal term term
+    | Apply p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | The range of a term is an element of a set.
+data PTerm v f
+    = Var v                         -- ^ A variable, either free or
+                                    -- bound by an enclosing quantifier.
+    | FunApp f [PTerm v f]           -- ^ Function application.
+                                    -- Constants are encoded as
+                                    -- nullary functions.  The result
+                                    -- is another term.
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+instance Negatable (Formula v p f) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants p => Constants (Formula v p f) where
+    fromBool = Predicate . fromBool
+    asBool (Predicate x) = asBool x
+    asBool _ = Nothing
+
+instance Constants p => Constants (Predicate p (PTerm v f)) where
+    fromBool x = Apply (fromBool x) []
+    asBool (Apply p _) = asBool p
+    asBool _ = Nothing
+
+instance (Constants (Formula v p f) {-, Ord v, Ord p, Ord f-}) => Combinable (Formula v p f) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+instance (C.Predicate p, Function f v) => C.Formula (Formula v p f) (Predicate p (PTerm v f)) where
+    atomic = Predicate
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v, Constants (Formula v p f), Combinable (Formula v p f)
+         ) => PropositionalFormula (Formula v p f) (Predicate p (PTerm v f)) where
+    foldPropositional co tf at formula =
+        maybe testFm tf (asBool formula)
+        where
+          testFm =
+              case formula of
+                Quant _ _ _ -> error "foldF0: quantifiers should not be present"
+                Combine x -> co x
+                Predicate x -> at x
+
+instance (Variable v, Function f v) => Term (PTerm v f) v f where
+    foldTerm vf fn t =
+        case t of
+          Var v -> vf v
+          FunApp f ts -> fn f ts
+    zipTerms v f t1 t2 =
+        case (t1, t2) of
+          (Var v1, Var v2) -> v v1 v2
+          (FunApp f1 ts1, FunApp f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Var
+    fApp x args = FunApp x args
+
+{-
+instance (Arity p, Constants p) => Atom (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtom ap (Apply p ts) = ap p ts
+    foldAtom ap (Constant x) = ap (fromBool x) []
+    foldAtom _ _ = error "foldAtom Predicate"
+    zipAtoms ap (Apply p1 ts1) (Apply p2 ts2) = ap p1 ts1 p2 ts2
+    zipAtoms ap (Constant x) (Constant y) = ap (fromBool x) [] (fromBool y) []
+    zipAtoms _ _ _ = error "zipAtoms Predicate"
+    apply' = Apply
+-}
+
+instance C.Predicate p => AtomEq (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtomEq ap tf _ (Apply p ts) = maybe (ap p ts) tf (asBool p)
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    equals = Equal
+    applyEq' = Apply
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          AtomEq (Predicate p (PTerm v f)) p (PTerm v f),
+          Constants (Formula v p f),
+          Variable v, C.Predicate p, Function f v
+         ) => FirstOrderFormula (Formula v p f) (Predicate p (PTerm v f)) v where
+    for_all v x = Quant Forall v x
+    exists v x = Quant Exists v x
+    foldFirstOrder qu co tf at f =
+        maybe testFm tf (asBool f)
+            where testFm = case f of
+                             Quant op v f' -> qu op v f'
+                             Combine x -> co x
+                             Predicate x -> at x
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Quant q1 v1 f1', Quant q2 v2 f2') -> qu q1 v1 (Quant q1 v1 f1') q2 v2 (Quant q2 v2 f2')
+          (Combine x, Combine y) -> co x y
+          (Predicate x, Predicate y) -> at x y
+          _ -> Nothing
+
+instance (Constants (Formula v p f),
+          Variable v, Ord v, Data v, Show v,
+          Arity p, Constants p, Ord p, Data p, Show p,
+          Skolem f, Ord f, Data f, Show f) => Literal (Formula v p f) (Predicate p (PTerm v f)) v where
+    foldLiteral co tf at l =
+        case l of
+          (Combine ((:~:) x)) -> co x
+          (Predicate p) -> at p
+          _ -> error "Literal (Formula v p f)"
+    atomic = Predicate
+-}
+
+instance (Constants p, Ord v, Ord p, Ord f, Constants (Predicate p (PTerm v f)), C.Formula (Formula v p f) (Predicate p (PTerm v f))
+         ) => Literal (Formula v p f) (Predicate p (PTerm v f)) where
+    foldLiteral neg tf at f =
+        case f of
+          Quant _ _ _ -> error "Invalid literal"
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error "Invalid literal"
+          Predicate p -> if p == fromBool True
+                         then tf True
+                         else if p == fromBool False
+                              then tf False
+                              else at p
+
+instance (C.Predicate p, Variable v, Function f v) => C.Atom (Predicate p (PTerm v f)) (PTerm v f) v where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (Apply _ ts) = foldr f r ts
+    foldTerms f r (Equal t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
+
+instance (Variable v, Pretty v,
+          C.Predicate p, Pretty p,
+          Function f v, Pretty f) => Pretty (Predicate p (PTerm v f)) where
+    pretty atom = prettyAtomEq pretty pretty pretty 0 atom
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          C.Predicate p, Variable v, Function f v, HasFixity (Predicate p (PTerm v f))) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v) => Pretty (Formula v p f) where
+    pretty f = prettyFirstOrder (\ _ -> pretty) pretty 0 $ f
+
+instance HasFixity (Predicate p term) where
+    fixity = const botFixity
+
+$(deriveSafeCopy 1 'base ''PTerm)
+$(deriveSafeCopy 1 'base ''Formula)
+$(deriveSafeCopy 2 'extension ''Predicate)
+
+-- Migration --
+
+data Predicate_v1 p term
+    = Equal_v1 term term
+    | NotEqual_v1 term term
+    | Constant_v1 Bool
+    | Apply_v1 p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+$(deriveSafeCopy 1 'base ''Predicate_v1)
+
+instance (SafeCopy p, SafeCopy term) => Migrate (Predicate p term) where
+    type MigrateFrom (Predicate p term) = (Predicate_v1 p term)
+    migrate (Equal_v1 t1 t2) = Equal t1 t2
+    migrate (Apply_v1 p ts) = Apply p ts
+    migrate (NotEqual_v1 _ _) = error "Failure migrating Predicate NotEqual"
+    migrate (Constant_v1 _) = error "Failure migrating Predicate Constant"
diff --git a/testdata/split-expected/Data/Logic/Types/FirstOrderPublic.hs b/testdata/split-expected/Data/Logic/Types/FirstOrderPublic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/FirstOrderPublic.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS -Wwarn -fno-warn-orphans #-}
+-- |An instance of FirstOrderFormula which implements Eq and Ord by comparing
+-- after conversion to normal form.  This helps us notice that formula which
+-- only differ in ways that preserve identity, e.g. swapped arguments to a
+-- commutative operator.
+
+module Data.Logic.Types.FirstOrderPublic
+    ( Formula(..)
+    , Bijection(..)
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, fixityFirstOrder)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(fixity))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Normal.Implicative (implicativeNormalForm, ImplicativeForm, runNormal)
+import qualified Data.Logic.Types.FirstOrder as N
+import Data.SafeCopy (base, deriveSafeCopy)
+import Data.Set (Set)
+import Data.Typeable (Typeable)
+
+-- |Convert between the public and internal representations.
+class Bijection p i where
+    public :: i -> p
+    intern :: p -> i
+
+-- |The new Formula type is just a wrapper around the Native instance
+-- (which eventually should be renamed the Internal instance.)  No
+-- derived Eq or Ord instances.
+data Formula v p f = Formula {unFormula :: N.Formula v p f} deriving (Data, Typeable, Show)
+
+instance Bijection (Formula v p f) (N.Formula v p f) where
+    public = Formula
+    intern = unFormula
+
+instance Bijection (Combination (Formula v p f)) (Combination (N.Formula v p f)) where
+    public (BinOp x op y) = BinOp (public x) op (public y)
+    public ((:~:) x) = (:~:) (public x)
+    intern (BinOp x op y) = BinOp (intern x) op (intern y)
+    intern ((:~:) x) = (:~:) (intern x)
+
+instance Negatable (Formula v p f) where
+    negatePrivate = Formula . negatePrivate . unFormula
+    foldNegation normal inverted = foldNegation (normal . Formula) (inverted . Formula) . unFormula
+
+instance (Constants (N.Formula v p f), Predicate p, Variable v, Function f v) => Constants (Formula v p f) where
+    fromBool = Formula . fromBool
+    asBool = asBool . unFormula
+
+instance (C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Constants (Formula v p f),
+          Constants (N.Formula v p f),
+          Variable v, Predicate p, Function f v) => Combinable (Formula v p f) where
+    x .<=>. y = Formula $ (unFormula x) .<=>. (unFormula y)
+    x .=>.  y = Formula $ (unFormula x) .=>. (unFormula y)
+    x .|.   y = Formula $ (unFormula x) .|. (unFormula y)
+    x .&.   y = Formula $ (unFormula x) .&. (unFormula y)
+
+instance (Predicate p, Function f v) => C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    atomic = Formula . C.atomic
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v where
+    for_all v x = public $ for_all v (intern x :: N.Formula v p f)
+    exists v x = public $ exists v (intern x :: N.Formula v p f)
+    foldFirstOrder qu co tf at f = foldFirstOrder qu' co' tf at (intern f :: N.Formula v p f)
+        where qu' quant v form = qu quant v (public form)
+              co' x = co (public x)
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Show v, Show p, Show f, HasFixity (Formula v p f), Variable v, Predicate p,
+          Function f v) => PropositionalFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    foldPropositional co tf at f = foldPropositional co' tf at (intern f :: N.Formula v p f)
+        where co' x = co (public x)
+
+-- |Here are the magic Ord and Eq instances
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v) => Ord (Formula v p f) where
+    compare a b =
+        let (a' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula a))
+            (b' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula b)) in
+        case compare a' b' of
+          EQ -> EQ
+          x -> {- if isRenameOf a' b' then EQ else -} x
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v, Constants (N.Predicate p (N.PTerm v f)),
+          FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v) => Eq (Formula v p f) where
+    a == b = compare a b == EQ
+
+instance (Predicate p, Function f v) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Pretty v, Show v, Variable v,
+          Pretty p, Show p, Predicate p,
+          Pretty f, Show f, Function f v) => Pretty (Formula v p f) where
+    pretty formula = prettyFirstOrder (\ _prec a -> pretty a) pretty 0 formula
+
+$(deriveSafeCopy 1 'base ''Formula)
diff --git a/testdata/split-expected/Data/Logic/Types/Harrison/Equal.hs b/testdata/split-expected/Data/Logic/Types/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Harrison/Equal.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Types.Harrison.Equal where
+
+-- ========================================================================= 
+-- First order logic with equality.                                          
+--                                                                           
+-- Copyright (co) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(fromBool))
+import Data.Logic.Classes.Equals (AtomEq(..), showFirstOrderFormulaEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (fixityFirstOrder)
+import qualified Data.Logic.Classes.Formula as C (Formula)
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.Logic.Types.Harrison.FOL (TermType(..))
+import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import Data.String (IsString(..))
+import Text.PrettyPrint (cat, text)
+
+data FOLEQ = EQUALS TermType TermType | R String [TermType] deriving (Eq, Ord, Show)
+data PredName = (:=:) | Named String deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Arity PredName where
+    arity (:=:) = Just 2
+    arity _ = Nothing
+
+instance Show (Formula FOLEQ) where
+    show = showFirstOrderFormulaEq
+
+instance HasFixity FOLEQ where
+    fixity (EQUALS _ _) = Fixity 5 InfixL
+    fixity _ = Fixity 10 InfixN
+
+instance IsString PredName where
+    fromString "=" = (:=:)
+    fromString s = Named s
+
+instance Constants PredName where
+    fromBool True = Named "true"
+    fromBool False = Named "false"
+    asBool x
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOLEQ where
+    fromBool x = R (fromBool x) []
+    asBool (R p _)
+        | fromBool True == p = Just True
+        | fromBool False == p = Just False
+        | True = Nothing
+    asBool _ = Nothing
+
+instance Predicate PredName
+
+instance Pretty PredName where
+    pretty (:=:) = text "="
+    pretty (Named s) = text s
+
+-- | Using PredName for the predicate type is not quite appropriate
+-- here, but we need to implement this instance so we can use it as a
+-- superclass of AtomEq below.
+instance Apply FOLEQ PredName TermType where
+    foldApply f _ (EQUALS t1 t2) = f (:=:) [t1, t2]
+    foldApply f tf (R p ts) = maybe (f (Named p) ts) tf (asBool (Named p))
+    apply' (Named p) ts = R p ts
+    apply' (:=:) [t1, t2] = EQUALS t1 t2
+    apply' (:=:) _ = error "arity"
+
+{-
+instance FirstOrderFormula (Formula FOLEQ) FOLEQ String where
+    exists = Exists
+    for_all = Forall
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+    atomic = Atom
+-}
+
+instance C.Formula (Formula FOLEQ) FOLEQ => P.PropositionalFormula (Formula FOLEQ) FOLEQ where
+    foldPropositional co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall _ _ -> error "quantifier in propositional formula"
+          Exists _ _ -> error "quantifier in propositional formula"
+
+instance Pretty FOLEQ where
+    pretty (EQUALS a b) = cat [pretty a, pretty (:=:), pretty b]
+    pretty (R s ts) = cat ([pretty s, pretty "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance HasFixity (Formula FOLEQ) where
+    fixity = fixityFirstOrder
+
+instance C.Formula (Formula FOLEQ) FOLEQ => Literal (Formula FOLEQ) FOLEQ where
+    foldLiteral neg tf at lit =
+        case lit of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> neg fm'
+          _ -> error "Literal (Formula FOLEQ)"
+
+-- instance PredicateEq PredName where
+--     eqp = (:=:)
+
+instance AtomEq FOLEQ PredName TermType where
+    foldAtomEq pr tf _ (R p ts) = maybe (pr (Named p) ts) tf (asBool (Named p))
+    foldAtomEq _ _ eq (EQUALS t1 t2) = eq t1 t2
+    equals = EQUALS
+    applyEq' (Named s) ts = R s ts
+    applyEq' (:=:) [t1, t2] = EQUALS t1 t2
+    applyEq' _ _ = error "arity"
+
+instance C.Atom FOLEQ TermType String where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (R _ ts) = foldr f r ts
+    foldTerms f r (EQUALS t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
diff --git a/testdata/split-expected/Data/Logic/Types/Harrison/FOL.hs b/testdata/split-expected/Data/Logic/Types/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Harrison/FOL.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Data.Logic.Types.Harrison.FOL
+    ( TermType(..)
+    , FOL(..)
+    , Function(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+--import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool)
+--import Data.Logic.Classes.FirstOrder (foldAtomsFirstOrder, mapAtomsFirstOrder)
+--import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Term(vt, foldTerm, fApp))
+import qualified Data.Logic.Classes.Term as C
+--import qualified Data.Logic.Classes.FirstOrder as C
+--import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import qualified Data.Logic.Types.Common ({- instance Variable String -})
+import Prelude hiding (pred)
+import Text.PrettyPrint (text, cat)
+
+-- -------------------------------------------------------------------------
+-- Terms.                                                                   
+-- -------------------------------------------------------------------------
+
+data TermType
+    = Var String
+    | Fn Function [TermType]
+    deriving (Eq, Ord)
+
+data FOL = R String [TermType] deriving (Eq, Ord, Show)
+
+instance Show TermType where
+    show (Var v) = "vt " ++ show v
+    show (Fn f ts) = "fApp " ++ show f ++ " " ++ show ts
+
+instance Pretty TermType where
+    pretty (Var v) = pretty v
+    pretty (Fn f ts) = cat ([pretty f, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Apply FOL String TermType where
+    foldApply f tf (R p ts) = maybe (f p ts) tf (asBool p)
+    apply' = R
+
+-- | This is probably dangerous.
+instance Constants String where
+    fromBool True = "true"
+    fromBool False = "false"
+    asBool x 
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOL where
+    fromBool x = R (fromBool x) []
+    asBool (R p _) = asBool p
+
+instance Predicate String
+
+{-
+instance Pretty String where
+    pretty = text
+
+instance FirstOrderFormula (Formula FOL) FOL String where
+    -- type C.Term (Formula FOL) = Term
+    -- type V (Formula FOL) = String
+    -- type Pr (Formula FOL) = String
+    -- type Fn (Formula FOL) = String -- ^ Atomic function type
+
+    -- quant C.Exists v fm = H.Exists v fm
+    -- quant C.Forall v fm = H.Forall v fm
+    for_all = H.Forall
+    exists = H.Exists
+    atomic = Atom
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          H.Forall v fm' -> qu C.Forall v fm'
+          H.Exists v fm' -> qu C.Exists v fm'
+-}
+
+instance Pretty FOL where
+    pretty (R p ts) = cat ([pretty p, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Arity String where
+    arity _ = Nothing
+
+-- | The Harrison book uses String for atomic function, but we need
+-- something a little more type safe because of our Skolem class.
+data Function
+    = FName String
+    | Skolem String
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+instance Pretty Function where
+    pretty (FName s) = text s
+    pretty (Skolem v) = text ("sK" ++ v)
+
+instance C.Function Function String
+
+instance Skolem Function String where
+    toSkolem = Skolem
+    isSkolem (Skolem _) = True
+    isSkolem _ = False
+
+instance Term TermType String Function where
+    -- type V Term = String
+    -- type Fn Term = String
+    vt = Var
+    fApp = Fn
+    foldTerm vfn _ (Var x) = vfn x
+    foldTerm _ ffn (Fn f ts) = ffn f ts
+    zipTerms = undefined
+
+instance HasFixity FOL where
+    fixity = const (Fixity 10 InfixN)
diff --git a/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs b/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.FirstOrder
+    ( Formula(..)
+    ) where
+
+--import Data.Char (isDigit)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder)
+import qualified Data.Logic.Classes.FirstOrder as C
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity)
+import Data.Logic.Types.Common ({- instance Variable String -})
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    | Forall String (Formula a)
+    | Exists String (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Constants a, Pretty a, HasFixity a) => C.Formula (Formula a) a where
+    atomic = Atom
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula a) a, Constants a, Pretty a, HasFixity a) => FirstOrderFormula (Formula a) a String where
+    for_all = Forall
+    exists = Exists
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+
+instance (FirstOrderFormula (Formula a) a String) => Pretty (Formula a) where
+    pretty = prettyFirstOrder (const pretty) pretty 0
diff --git a/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs b/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.Propositional
+    ( Formula(..)
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (Formula atom), Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    -- The atom type for this formula is the same as its first type parameter.
+    foldPropositional co tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> co ((:~:) f)
+          And f g -> co (BinOp f (:&:) g)
+          Or f g -> co (BinOp f (:|:) g)
+          Imp f g -> co (BinOp f (:=>:) g)
+          Iff f g -> co (BinOp f (:<=>:) g)
+          Atom x -> at x
+
+instance (HasFixity atom, Pretty atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> neg f
+          Atom x -> at x
+          _ -> error ("Unexpected literal " ++ show (pretty formula))
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split-expected/Data/Logic/Types/Harrison/Prop.hs b/testdata/split-expected/Data/Logic/Types/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Harrison/Prop.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Prop
+    ( Prop(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Pretty
+import Data.Logic.Classes.Propositional (showPropositional)
+import Data.Logic.Types.Harrison.Formulas.Propositional (Formula(..))
+import Prelude hiding (negate)
+import Text.PrettyPrint (text)
+
+-- =========================================================================
+-- Basic stuff for propositional logic: datatype, parsing and printing.     
+-- =========================================================================
+
+newtype Prop = P {pname :: String} deriving (Read, Data, Typeable, Eq, Ord)
+
+instance Show Prop where
+    show x = "P " ++ show (pname x)
+
+instance Pretty Prop where
+    pretty = text . pname
+
+instance HasFixity String where
+    fixity = const botFixity
+
+instance HasFixity Prop where
+    fixity = const botFixity
+
+instance Show (Formula Prop) where
+    show = showPropositional show
+
+instance Show (Formula String) where
+    show = showPropositional show
diff --git a/testdata/split-expected/Data/Logic/Types/Propositional.hs b/testdata/split-expected/Data/Logic/Types/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-expected/Data/Logic/Types/Propositional.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+module Data.Logic.Types.Propositional where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula atom
+    = Combine (Combination (Formula atom))
+    | Atom atom
+    | T
+    | F
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq,Ord,Data,Typeable)
+
+instance Negatable (Formula atom) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Ord atom) => Combinable (Formula atom) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+
+instance Constants (Formula atom) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error ("Unexpected literal: " ++ show (pretty formula))
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (C.Formula (Formula atom) atom, Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    foldPropositional co tf at formula =
+        case formula of
+          Combine x -> co x
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split-merge-expected/Data/Boolean.hs b/testdata/split-merge-expected/Data/Boolean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Boolean.hs
@@ -0,0 +1,157 @@
+{-# OPTIONS -fno-warn-incomplete-patterns #-}
+-- |
+-- Module      : Data.Boolean
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This library provides a representation of boolean formulas that is
+-- used by the solver in "Data.Boolean.SatSolver".
+-- 
+-- We also define a function to simplify formulas, a type for
+-- conjunctive normalforms, and a function that creates them from
+-- boolean formulas.
+-- 
+module Data.Boolean ( 
+
+  Boolean(..), 
+
+  Literal(..), literalVar, invLiteral, isPositiveLiteral, 
+
+  CNF, Clause, booleanToCNF
+
+  ) where
+
+import Data.Maybe ( mapMaybe )
+import qualified Data.IntMap as IM
+
+import Control.Monad ( guard, liftM )
+
+-- | Boolean formulas are represented as values of type @Boolean@.
+-- 
+data Boolean
+  -- | Variables are labeled with an @Int@,
+  = Var Int
+  -- | @Yes@ represents /true/,
+  | Yes
+  -- | @No@ represents /false/,
+  | No
+  -- | @Not@ constructs negated formulas,
+  | Not Boolean
+  -- | and finally we provide conjunction
+  | Boolean :&&: Boolean
+  -- | and disjunction of boolean formulas.
+  | Boolean :||: Boolean
+ deriving Show
+
+-- | Literals are variables that occur either positively or negatively.
+-- 
+data Literal = Pos Int | Neg Int deriving (Eq, Show)
+
+-- | This function returns the name of the variable in a literal.
+-- 
+literalVar :: Literal -> Int
+literalVar (Pos n) = n
+literalVar (Neg n) = n
+
+-- | This function negates a literal.
+-- 
+invLiteral :: Literal -> Literal
+invLiteral (Pos n) = Neg n
+invLiteral (Neg n) = Pos n
+
+-- | This predicate checks whether the given literal is positive.
+-- 
+isPositiveLiteral :: Literal -> Bool
+isPositiveLiteral (Pos _) = True
+isPositiveLiteral _       = False
+
+-- | Conjunctive normalforms are lists of lists of literals.
+-- 
+type CNF     = [Clause]
+type Clause  = [Literal]
+
+-- | 
+-- We convert boolean formulas to conjunctive normal form by pushing
+-- negations down to variables and repeatedly applying the
+-- distributive laws.
+-- 
+booleanToCNF :: Boolean -> CNF
+booleanToCNF
+  = mapMaybe (simpleClause . map literal . disjunction)
+  . conjunction
+  . asLongAsPossible distribute
+  . asLongAsPossible pushNots
+  . asLongAsPossible elim
+ where
+  elim (Not Yes)      = Just No
+  elim (Not No)       = Just Yes
+  elim (No  :&&: _)   = Just No
+  elim (Yes :&&: x)   = Just x
+  elim (_   :&&: No)  = Just No
+  elim (x   :&&: Yes) = Just x 
+  elim (Yes :||: _)   = Just Yes
+  elim (No  :||: x)   = Just x
+  elim (_   :||: Yes) = Just Yes
+  elim (x   :||: No)  = Just x
+  elim _              = Nothing
+
+  pushNots (Not (Not x))  = Just x
+  pushNots (Not (x:&&:y)) = Just (Not x :||: Not y)
+  pushNots (Not (x:||:y)) = Just (Not x :&&: Not y)
+  pushNots _              = Nothing
+
+  distribute (x:||:(y:&&:z)) = Just ((x:||:y):&&:(x:||:z))
+  distribute ((x:&&:y):||:z) = Just ((x:||:z):&&:(y:||:z))
+  distribute _               = Nothing
+
+  literal (Var x)       = Pos x
+  literal (Not (Var x)) = Neg x
+
+
+-- private helper functions
+
+-- remove duplicate literals from clauses and drop clauses that
+-- contain one literal both positively and negatively.
+--
+simpleClause :: Clause -> Maybe Clause
+simpleClause = liftM (map lit . IM.toList) . foldl add (Just IM.empty)
+ where
+  lit (x,True)  = Pos x
+  lit (x,False) = Neg x
+
+  add mm l = do
+    m <- mm
+    let x = literalVar l; kind = isPositiveLiteral l
+    maybe (Just (IM.insert x kind m))
+          (\b -> guard (b==kind) >> Just m)
+          (IM.lookup x m)
+
+conjunction :: Boolean -> [Boolean]
+conjunction b = flat b []
+ where flat Yes      = id
+       flat (x:&&:y) = flat x . flat y
+       flat x        = (x:)
+
+disjunction :: Boolean -> [Boolean]
+disjunction b = flat b []
+ where flat No       = id
+       flat (x:||:y) = flat x . flat y
+       flat x        = (x:)
+
+asLongAsPossible :: (Boolean -> Maybe Boolean) -> Boolean -> Boolean
+asLongAsPossible f = everywhere g
+ where g x = maybe x (everywhere g) (f x)
+
+everywhere :: (Boolean -> Boolean) -> Boolean -> Boolean
+everywhere f = f . atChildren (everywhere f)
+
+atChildren :: (Boolean -> Boolean) -> Boolean -> Boolean
+atChildren f (Not x)  = Not (f x)
+atChildren f (x:&&:y) = f x :&&: f y
+atChildren f (x:||:y) = f x :||: f y
+atChildren _ x        = x
+
diff --git a/testdata/split-merge-expected/Data/Boolean/SatSolver.hs b/testdata/split-merge-expected/Data/Boolean/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Boolean/SatSolver.hs
@@ -0,0 +1,183 @@
+-- |
+-- Module      : Data.Boolean.SatSolver
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This Haskell library provides an implementation of the
+-- Davis-Putnam-Logemann-Loveland algorithm
+-- (cf. <http://en.wikipedia.org/wiki/DPLL_algorithm>) for the boolean
+-- satisfiability problem. It not only allows to solve boolean
+-- formulas in one go but also to add constraints and query bindings
+-- of variables incrementally.
+-- 
+-- The implementation is not sophisticated at all but uses the basic
+-- DPLL algorithm with unit propagation.
+-- 
+module Data.Boolean.SatSolver (
+
+  Boolean(..), SatSolver, Literal(..), literalVar, invLiteral, isPositiveLiteral, CNF, Clause, booleanToCNF,
+
+  newSatSolver, isSolved, 
+
+  lookupVar, assertTrue, assertTrue', branchOnVar, selectBranchVar, solve, isSolvable
+
+  ) where
+
+import Data.List
+import Data.Boolean
+
+import Control.Monad.Writer
+
+import qualified Data.IntMap as IM
+
+-- | A @SatSolver@ can be used to solve boolean formulas.
+-- 
+data SatSolver = SatSolver { clauses :: CNF, bindings :: IM.IntMap Bool }
+ deriving Show
+
+-- | A new SAT solver without stored constraints.
+-- 
+newSatSolver :: SatSolver
+newSatSolver = SatSolver [] IM.empty
+
+-- | This predicate tells whether all constraints are solved.
+-- 
+isSolved :: SatSolver -> Bool
+isSolved = null . clauses
+
+-- |
+-- We can lookup the binding of a variable according to the currently
+-- stored constraints. If the variable is unbound, the result is
+-- @Nothing@.
+-- 
+lookupVar :: Int -> SatSolver -> Maybe Bool
+lookupVar name = IM.lookup name . bindings
+
+-- | 
+-- We can assert boolean formulas to update a @SatSolver@. The
+-- assertion may fail if the resulting constraints are unsatisfiable.
+-- 
+assertTrue :: MonadPlus m => Boolean -> SatSolver -> m SatSolver
+assertTrue formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      (booleanToCNF formula)
+  simplify (solver { clauses = newClauses })
+
+assertTrue' :: MonadPlus m => CNF -> SatSolver -> m SatSolver
+assertTrue' formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      formula
+  simplify (solver { clauses = newClauses })
+
+-- |
+-- This function guesses a value for the given variable, if it is
+-- currently unbound. As this is a non-deterministic operation, the
+-- resulting solvers are returned in an instance of @MonadPlus@.
+-- 
+branchOnVar :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnVar name solver =
+  maybe (branchOnUnbound name solver)
+        (const (return solver))
+        (lookupVar name solver)
+
+-- |
+-- We select a variable from the shortest clause hoping to produce a
+-- unit clause.
+--
+selectBranchVar :: SatSolver -> Int
+selectBranchVar = literalVar . head . head . sortBy shorter . clauses
+
+-- | 
+-- This function guesses values for variables such that the stored
+-- constraints are satisfied. The result may be non-deterministic and
+-- is, hence, returned in an instance of @MonadPlus@.
+-- 
+solve :: MonadPlus m => SatSolver -> m SatSolver
+solve solver
+  | isSolved solver = return solver
+  | otherwise = branchOnUnbound (selectBranchVar solver) solver >>= solve
+
+-- |
+-- This predicate tells whether the stored constraints are
+-- solvable. Use with care! This might be an inefficient operation. It
+-- tries to find a solution using backtracking and returns @True@ if
+-- and only if that fails.
+-- 
+isSolvable :: SatSolver -> Bool
+isSolvable = not . null . solve
+
+
+-- private helper functions
+
+addClause :: MonadPlus m => IM.IntMap Bool -> m [Clause] -> Clause -> m [Clause]
+addClause binds mclauses newClause = do
+  oldClauses <- mclauses
+  let unboundLits = foldl (addUnbound binds) (Just []) newClause
+  maybe (return oldClauses)
+        (\lits -> guard (not (null lits)) >> return (lits:oldClauses))
+        unboundLits
+
+addUnbound :: IM.IntMap Bool -> Maybe Clause -> Literal -> Maybe Clause
+addUnbound binds mlits lit = do
+  lits <- mlits
+  maybe (Just (lit:lits))
+        (\b -> guard (b /= isPositiveLiteral lit) >> return lits)
+        (IM.lookup (literalVar lit) binds)
+
+updateSolver :: MonadPlus m => CNF -> [(Int,Bool)] -> SatSolver -> m SatSolver
+updateSolver cs bs solver = do
+  bs' <- foldr (uncurry insertBinding) (return (bindings solver)) bs
+  return $ solver { clauses = cs, bindings = bs' }
+
+insertBinding :: MonadPlus m
+              => Int -> Bool -> m (IM.IntMap Bool) -> m (IM.IntMap Bool)
+insertBinding name newValue binds = do
+  bs <- binds
+  maybe (return (IM.insert name newValue bs))
+        (\oldValue -> do guard (oldValue==newValue); return bs)
+        (IM.lookup name bs)
+
+simplify :: MonadPlus m => SatSolver -> m SatSolver
+simplify solver = do
+  (cs,bs) <- runWriterT . simplifyClauses . clauses $ solver
+  updateSolver cs bs solver
+
+simplifyClauses :: MonadPlus m => CNF -> WriterT [(Int,Bool)] m CNF
+simplifyClauses [] = return []
+simplifyClauses allClauses = do
+  let shortestClause = head . sortBy shorter $ allClauses
+  guard (not (null shortestClause))
+  if null (tail shortestClause)
+   then propagate (head shortestClause) allClauses >>= simplifyClauses
+   else return allClauses
+
+propagate :: MonadPlus m => Literal -> CNF -> WriterT [(Int,Bool)] m CNF
+propagate literal allClauses = do
+  tell [(literalVar literal, isPositiveLiteral literal)]
+  return (foldr prop [] allClauses)
+ where
+  prop c cs | literal `elem` c = cs
+            | otherwise        = filter (invLiteral literal/=) c : cs
+
+branchOnUnbound :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnUnbound name solver =
+  guess (Pos name) solver `mplus` guess (Neg name) solver
+
+guess :: MonadPlus m => Literal -> SatSolver -> m SatSolver
+guess literal solver = do
+  (cs,bs) <- runWriterT (propagate literal (clauses solver) >>= simplifyClauses)
+  updateSolver cs bs solver
+
+shorter :: [a] -> [a] -> Ordering
+shorter []     []     = EQ
+shorter []     _      = LT
+shorter _      []     = GT
+shorter (_:xs) (_:ys) = shorter xs ys
+
+
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Apply.hs b/testdata/split-merge-expected/Data/Logic/Classes/Apply.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Apply.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures #-}
+-- | The Apply class represents a type of atom the only supports predicate application.
+module Data.Logic.Classes.Apply
+    ( Apply(..)
+    , Predicate
+    , apply
+    , zipApplys
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , showApply
+    , prettyApply
+    , varApply
+    , substApply
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Term (Term, showTerm, prettyTerm, fvt, tsubst)
+import Data.List (intercalate, intersperse)
+import Data.Maybe (fromMaybe)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), text, empty, parens, cat)
+
+class (Arity p, Constants p, Eq p, Ord p, Data p, Pretty p) => Predicate p
+
+class Predicate p => Apply atom p term | atom -> p term where
+    foldApply :: (p -> [term] -> r) -> (Bool -> r) -> atom -> r
+    apply' :: p -> [term] -> atom
+
+-- | apply' with an arity check - clients should always call this.
+apply :: Apply atom p term => p -> [term] -> atom
+apply p ts =
+    case arity p of
+      Just n | n /= length ts -> error "arity"
+      _ -> apply' p ts
+
+zipApplys :: Apply atom p term =>
+            (p -> [term] -> p -> [term] -> Maybe r)
+         -> (Bool -> Bool -> Maybe r)
+         -> atom -> atom -> Maybe r
+zipApplys ap tf a1 a2 =
+    foldApply ap' tf' a1
+    where
+      ap' p1 ts1 = foldApply (ap p1 ts1) (\ _ -> Nothing) a2
+      tf' x1 = foldApply (\ _ _ -> Nothing) (tf x1) a2
+
+apply0 p = if fromMaybe 0 (arity p) == 0 then apply' p [] else error "arity"
+apply1 p a = if fromMaybe 1 (arity p) == 1 then apply' p [a] else error "arity"
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then apply' p [a,b] else error "arity"
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then apply' p [a,b,c] else error "arity"
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then apply' p [a,b,c,d] else error "arity"
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then apply' p [a,b,c,d,e] else error "arity"
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then apply' p [a,b,c,d,e,f] else error "arity"
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then apply' p [a,b,c,d,e,f,g] else error "arity"
+
+showApply :: (Apply atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showApply =
+    foldApply (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+              (\ x -> if x then "true" else "false")
+
+prettyApply :: (Apply atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyApply pv pp pf prec atom =
+    foldApply (\ p ts ->
+                   pp p <> case ts of
+                             [] -> empty
+                             _ -> parens (cat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+              (\ x -> text (if x then "true" else "false"))
+              atom
+
+-- | Return the variables that occur in an instance of Apply.
+varApply :: (Apply atom p term, Term term v f) => atom -> Set.Set v
+varApply = foldApply (\ _ args -> Set.unions (map fvt args)) (const Set.empty)
+
+substApply :: (Apply atom p term, Constants atom, Term term v f) => Map.Map v term -> atom -> atom
+substApply env = foldApply (\ p args -> apply p (map (tsubst env) args)) fromBool
+
+{-
+instance (Apply atom p term, Term term v f, Constants atom) => Formula atom term v where
+    allVariables = varApply
+    freeVariables = varApply
+    substitute = substApply
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Arity.hs b/testdata/split-merge-expected/Data/Logic/Classes/Arity.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Arity.hs
@@ -0,0 +1,11 @@
+module Data.Logic.Classes.Arity
+    ( Arity(arity)
+    ) where
+
+-- |A class that characterizes how many arguments a predicate or
+-- function takes.  Depending on the context, a result of Nothing may
+-- mean that the arity is undetermined or unknown.  However, even if
+-- this returns Nothing, the same number of arguments must be passed
+-- to all uses of a given predicate or function.
+class Arity p where
+    arity :: p -> Maybe Int
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Atom.hs b/testdata/split-merge-expected/Data/Logic/Classes/Atom.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Atom.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, TypeFamilies #-}
+-- | Substitution and finding variables are two basic operations on
+-- formulas that contain terms and variables.  If a formula type
+-- supports quantifiers we can also find free variables, otherwise all
+-- variables are considered free.
+module Data.Logic.Classes.Atom
+    ( Atom(..)
+    -- , Formula(..)
+    ) where
+
+import Control.Applicative.Error (Failing)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+{-
+class Formula formula term v | formula -> term v where
+    substitute :: Map.Map v term -> formula -> formula
+    allVariables :: formula -> Set.Set v
+    freeVariables :: formula -> Set.Set v
+    unify :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    match :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> formula -> r
+    isRename :: formula -> formula -> Bool
+    getSubst :: Map.Map v term -> formula -> Map.Map v term
+-}
+
+class Atom atom term v | atom -> term v where
+    substitute :: Map.Map v term -> atom -> atom
+    allVariables :: atom -> Set.Set v
+    freeVariables :: atom -> Set.Set v
+    unify :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    match :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> atom -> r
+    isRename :: atom -> atom -> Bool
+    getSubst :: Map.Map v term -> atom -> Map.Map v term
+
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs b/testdata/split-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.ClauseNormalForm
+    ( ClauseNormalFormula(clauses, makeCNF, satisfiable)
+    ) where
+
+import Control.Monad (MonadPlus)
+import Data.Logic.Classes.Negate
+import Data.Set as S
+
+-- |A class to represent formulas in CNF, which is the conjunction of
+-- a set of disjuncted literals each which may or may not be negated.
+class (Negatable lit, Eq lit, Ord lit) => ClauseNormalFormula cnf lit | cnf -> lit where
+    clauses :: cnf -> S.Set (S.Set lit)
+    makeCNF :: S.Set (S.Set lit) -> cnf
+    satisfiable :: MonadPlus m => cnf -> m Bool
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Combine.hs b/testdata/split-merge-expected/Data/Logic/Classes/Combine.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Combine.hs
@@ -0,0 +1,123 @@
+-- | Class Logic defines the basic boolean logic operations,
+-- AND, OR, NOT, and so on.  Definitions which pertain to both
+-- propositional and first order logic are here.
+{-# LANGUAGE DeriveDataTypeable #-}
+module Data.Logic.Classes.Combine
+    ( Combinable(..)
+    , Combination(..)
+    , combine
+    , BinOp(..)
+    , binop
+    -- * Unicode aliases for Combinable class methods
+    , (∧)
+    , (∨)
+    , (⇒)
+    , (⇔)
+    -- * Use in Harrison's code
+    , (==>)
+    , (<=>)
+    , prettyBinOp
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Negate (Negatable, (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- | A type class for logical formulas.  Minimal implementation:
+-- @
+--  (.|.)
+-- @
+class (Negatable formula) => Combinable formula where
+    -- | Disjunction/OR
+    (.|.) :: formula -> formula -> formula
+
+    -- | Derived formula combinators.  These could (and should!) be
+    -- overridden with expressions native to the instance.
+    --
+    -- | Conjunction/AND
+    (.&.) :: formula -> formula -> formula
+    x .&. y = (.~.) ((.~.) x .|. (.~.) y)
+    -- | Formula combinators: Equivalence
+    (.<=>.) :: formula -> formula -> formula
+    x .<=>. y = (x .=>. y) .&. (y .=>. x)
+    -- | Implication
+    (.=>.) :: formula -> formula -> formula
+    x .=>. y = ((.~.) x .|. y)
+    -- | Reverse implication:
+    (.<=.) :: formula -> formula -> formula
+    x .<=. y = y .=>. x
+    -- | Exclusive or
+    (.<~>.) :: formula -> formula -> formula
+    x .<~>. y = ((.~.) x .&. y) .|. (x .&. (.~.) y)
+    -- | Nor
+    (.~|.) :: formula -> formula -> formula
+    x .~|. y = (.~.) (x .|. y)
+    -- | Nand
+    (.~&.) :: formula -> formula -> formula
+    x .~&. y = (.~.) (x .&. y)
+
+infixl 1  .<=>. ,  .<~>., ⇔, <=>
+infixr 2  .=>., ⇒, ==>
+infixr 3  .|., ∨
+infixl 4  .&., ∧
+
+-- |'Combination' is a helper type used in the signatures of the
+-- 'foldPropositional' and 'foldFirstOrder' methods so can represent
+-- all the ways that formulas can be combined using boolean logic -
+-- negation, logical And, and so forth.
+data Combination formula
+    = BinOp formula BinOp formula
+    | (:~:) formula
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | A helper function for building folds:
+-- @
+--   foldPropositional combine atomic
+-- @
+-- is a no-op.
+combine :: Combinable formula => Combination formula -> formula
+combine (BinOp f1 (:<=>:) f2) = f1 .<=>. f2
+combine (BinOp f1 (:=>:) f2) = f1 .=>. f2
+combine (BinOp f1 (:&:) f2) = f1 .&. f2
+combine (BinOp f1 (:|:) f2) = f1 .|. f2
+combine ((:~:) f) = (.~.) f
+
+-- | Represents the boolean logic binary operations, used in the
+-- Combination type above.
+data BinOp
+    = (:<=>:)  -- ^ Equivalence
+    |  (:=>:)  -- ^ Implication
+    |  (:&:)  -- ^ AND
+    |  (:|:)  -- ^ OR
+    deriving (Eq, Ord, Data, Typeable, Enum, Bounded, Show, Read)
+
+binop :: Combinable formula => formula -> BinOp -> formula -> formula
+binop a (:&:) b = a .&. b
+binop a (:|:) b = a .|. b
+binop a (:=>:) b = a .=>. b
+binop a (:<=>:) b = a .<=>. b
+
+(∧) :: Combinable formula => formula -> formula -> formula
+(∧) = (.&.)
+(∨) :: Combinable formula => formula -> formula -> formula
+(∨) = (.|.)
+-- | ⇒ can't be a function when -XUnicodeSyntax is enabled.
+(⇒) :: Combinable formula => formula -> formula -> formula
+(⇒) = (.=>.)
+(⇔) :: Combinable formula => formula -> formula -> formula
+(⇔) = (.<=>.)
+
+(==>) :: Combinable formula => formula -> formula -> formula
+(==>) = (.=>.)
+(<=>) :: Combinable formula => formula -> formula -> formula
+(<=>) = (.<=>.)
+
+prettyBinOp :: BinOp -> Doc
+prettyBinOp (:<=>:) = text "⇔"
+prettyBinOp (:=>:) = text "⇒"
+prettyBinOp (:&:) = text "∧"
+prettyBinOp (:|:) = text "∨"
+
+instance Pretty BinOp where
+    pretty = prettyBinOp
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Constants.hs b/testdata/split-merge-expected/Data/Logic/Classes/Constants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Constants.hs
@@ -0,0 +1,40 @@
+module Data.Logic.Classes.Constants
+    ( Constants(asBool, fromBool)
+    , ifElse
+    , true
+    , (⊨)
+    , false
+    , (⊭)
+    , prettyBool
+    ) where
+
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- |Some types in the Logic class heirarchy need to have True and
+-- False elements.
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+true :: Constants p => p
+true = fromBool True
+
+false :: Constants p => p
+false = fromBool False
+
+ifElse :: a -> a -> Bool -> a
+ifElse t _ True = t
+ifElse _ f False = f
+
+(⊨) :: Constants formula => formula
+(⊨) = true
+(⊭) :: Constants formula => formula
+(⊭) = false
+
+prettyBool :: Bool -> Doc
+prettyBool True = text "⊨"
+prettyBool False = text "⊭"
+
+instance Pretty Bool where
+    pretty = prettyBool
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Equals.hs b/testdata/split-merge-expected/Data/Logic/Classes/Equals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Equals.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+-- | Support for equality.
+module Data.Logic.Classes.Equals
+    ( AtomEq(..)
+    , applyEq
+    , PredicateName(..)
+    , zipAtomsEq
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , pApp, pApp0, pApp1, pApp2, pApp3, pApp4, pApp5, pApp6, pApp7
+    , showFirstOrderFormulaEq
+    , (.=.), (≡)
+    , (.!=.), (≢)
+    , fromAtomEq
+    , showAtomEq
+    , prettyAtomEq
+    , varAtomEq
+    , substAtomEq
+    , funcsAtomEq
+    ) where
+
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Data.Logic.Classes.Term (convertTerm, funcs, fvt, prettyTerm, showTerm, Term, tsubst)
+import qualified Data.Map as Map (Map)
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set (empty, Set, union, unions)
+import Text.PrettyPrint ((<+>), (<>), Doc, empty, hcat, nest, parens, text)
+
+-- | Its not safe to make Atom a superclass of AtomEq, because the Atom methods will fail on AtomEq instances.
+class Predicate p => AtomEq atom p term | atom -> p term, term -> atom p where
+    foldAtomEq :: (p -> [term] -> r) -> (Bool -> r) -> (term -> term -> r) -> atom -> r
+    equals :: term -> term -> atom
+    applyEq' :: p -> [term] -> atom
+
+-- | applyEq' with an arity check - clients should always call this.
+applyEq :: AtomEq atom p term => p -> [term] -> atom
+applyEq p ts =
+    case arity p of
+      Just n | n /= length ts -> arityError p ts
+      _ -> applyEq' p ts
+
+-- | A way to represent any predicate's name.  Frequently the equality
+-- predicate has no standalone representation in the p type, it is
+-- just a constructor in the atom type, or even the formula type.
+data PredicateName p = Named p Int | Equals deriving (Eq, Ord, Show)
+
+instance (Pretty p, Ord p) => Pretty (PredicateName p) where
+    pretty Equals = text "="
+    pretty (Named p _) = pretty p
+
+zipAtomsEq :: AtomEq atom p term =>
+              (p -> [term] -> p -> [term] -> Maybe r)
+           -> (Bool -> Bool -> Maybe r)
+           -> (term -> term -> term -> term -> Maybe r)
+           -> atom -> atom -> Maybe r
+zipAtomsEq ap tf eq a1 a2 =
+    foldAtomEq ap' tf' eq' a1
+    where
+      ap' p1 ts1 = foldAtomEq (ap p1 ts1) (\ _ -> Nothing) (\ _ _ -> Nothing) a2
+      tf' x1 = foldAtomEq (\ _ _ -> Nothing) (tf x1) (\ _ _ -> Nothing) a2
+      eq' t1 t2 = foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) (eq t1 t2) a2
+
+apply0 :: AtomEq atom p term => p -> atom
+apply0 p = if fromMaybe 0 (arity p) == 0 then applyEq' p [] else arityError p []
+apply1 :: AtomEq atom p a => p -> a -> atom
+apply1 p a = if fromMaybe 1 (arity p) == 1 then applyEq' p [a] else arityError p [a]
+apply2 :: AtomEq atom p a => p -> a -> a -> atom
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then applyEq' p [a,b] else arityError p [a,b]
+apply3 :: AtomEq atom p a => p -> a -> a -> a -> atom
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then applyEq' p [a,b,c] else arityError p [a,b,c]
+apply4 :: AtomEq atom p a => p -> a -> a -> a -> a -> atom
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then applyEq' p [a,b,c,d] else arityError p [a,b,c,d]
+apply5 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> atom
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then applyEq' p [a,b,c,d,e] else arityError p [a,b,c,d,e]
+apply6 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> atom
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then applyEq' p [a,b,c,d,e,f] else arityError p [a,b,c,d,e,f]
+apply7 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> a -> atom
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then applyEq' p [a,b,c,d,e,f,g] else arityError p [a,b,c,d,e,f,g]
+
+arityError :: (Arity p) => p -> [a] -> t
+arityError _p _ts = error "arity error"
+-- arityError :: (Arity p, Pretty p) => p -> [a] -> t
+-- arityError p ts = error $ "arity error: " ++ show (length ts) ++ " arguments applied to arity " ++ show (arity p) ++ " predicate " ++ show (pretty p)
+
+pApp :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> [term] -> formula
+pApp p ts = atomic (applyEq p ts)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a)
+pApp2 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b)
+pApp3 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c)
+pApp4 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d)
+pApp5 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e)
+pApp6 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f)
+pApp7 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g)
+
+showFirstOrderFormulaEq :: (FirstOrderFormula fof atom v, AtomEq atom p term, Show term, Show v, Show p) => fof -> String
+showFirstOrderFormulaEq fm =
+    fst (sfo fm)
+    where
+      sfo p = foldFirstOrder qu co tf pr p
+      qu op v f = (showQuant op ++ " " ++ show v ++ " " ++ parens quantPrec (sfo f), quantPrec)
+      co ((:~:) p) =
+          let prec' = 5 in
+          ("(.~.)" ++ parens prec' (sfo p), prec')
+      co (BinOp p op q) = (parens (opPrec op) (sfo p) ++ " " ++ showBinOp op ++ " " ++ parens (opPrec op) (sfo q), opPrec op)
+      tf x = (if x then "true" else "false", 0)
+      pr = foldAtomEq (\ p ts -> ("pApp " ++ show p ++ " " ++ show ts, 6))
+                      (\ x -> (if x then "true" else "false", 0))
+                      (\ t1 t2 -> ("(" ++ show t1 ++ ") .=. (" ++ show t2 ++ ")", 6))
+      showBinOp (:<=>:) = ".<=>."
+      showBinOp (:=>:) = ".=>."
+      showBinOp (:&:) = ".&."
+      showBinOp (:|:) = ".|."
+      showQuant Exists = "exists"
+      showQuant Forall = "for_all"
+      opPrec (:|:) = 3
+      opPrec (:&:) = 4
+      opPrec (:=>:) = 2
+      opPrec (:<=>:) = 2
+      quantPrec = 1
+      parens :: Int -> (String, Int) -> String
+      parens prec' (s, prec) = if prec >= prec' then "(" ++ s ++ ")" else s
+
+infix 5 .=., .!=., ≡, ≢
+
+(.=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .=. b = atomic (equals a b)
+
+(.!=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .!=. b = (.~.) (a .=. b)
+
+(≡) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≡) = (.=.)
+
+(≢) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≢) = (.!=.)
+
+{-
+instance (AtomEq atom p term, Constants atom, Variable v, Term term v f) => Formula atom term v where
+    substitute env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+    allVariables = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+    freeVariables = allVariables
+-}
+
+fromAtomEq :: (AtomEq atom1 p1 term1, Term term1 v1 f1,
+               AtomEq atom2 p2 term2, Term term2 v2 f2, Constants atom2) =>
+              (v1 -> v2) -> (p1 -> p2) -> (f1 -> f2) -> atom1 -> atom2
+fromAtomEq cv cp cf atom =
+    foldAtomEq (\ pr ts -> applyEq (cp pr) (map ct ts))
+               fromBool
+               (\ a b -> ct a `equals` ct b)
+               atom
+    where
+      ct = convertTerm cv cf
+
+showAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showAtomEq =
+    foldAtomEq (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+               (\ x -> if x then "true" else "false")
+               (\ t1 t2 -> "(" ++ parenTerm t1 ++ " .=. " ++ parenTerm t2 ++ ")")
+    where
+      parenTerm :: term -> String
+      parenTerm x = "(" ++ showTerm x ++ ")"
+
+prettyAtomEq :: (AtomEq atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyAtomEq pv pp pf prec atom =
+    foldAtomEq (\ p ts -> pp p <> case ts of
+                                    [] -> empty
+                                    _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+               (text . ifElse "true" "false")
+               (\ t1 t2 -> parensIf (prec > 6) (prettyTerm pv pf t1 <+> text "=" <+> prettyTerm pv pf t2))
+               atom
+    where
+      parensIf False = id
+      parensIf _ = parens . nest 1
+
+-- | Return the variables that occur in an instance of AtomEq.
+varAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f) => atom -> Set.Set v
+varAtomEq = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+
+substAtomEq :: (AtomEq atom p term, Constants atom, Term term v f) =>
+               Map.Map v term -> atom -> atom
+substAtomEq env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+
+funcsAtomEq :: (AtomEq atom p term, Term term v f, Ord f) => atom -> Set.Set (f, Int)
+funcsAtomEq = foldAtomEq (\ _ ts -> Set.unions (map funcs ts)) (const Set.empty) (\ t1 t2 -> Set.union (funcs t1) (funcs t2))
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/FirstOrder.hs b/testdata/split-merge-expected/Data/Logic/Classes/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/FirstOrder.hs
@@ -0,0 +1,354 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+module Data.Logic.Classes.FirstOrder
+    ( FirstOrderFormula(..)
+    , Quant(..)
+    , zipFirstOrder
+    , pApp
+    , pApp0
+    , pApp1
+    , pApp2
+    , pApp3
+    , pApp4
+    , pApp5
+    , pApp6
+    , pApp7
+    , for_all'
+    , exists'
+    , quant
+    , (!)
+    , (?)
+    , (∀)
+    , (∃)
+    , quant'
+    , convertFOF
+    , toPropositional
+    , withUnivQuants
+    , showFirstOrder
+    , prettyFirstOrder
+    , fixityFirstOrder
+    , foldAtomsFirstOrder
+    , mapAtomsFirstOrder
+    , onatoms
+    , overatoms
+    , atom_union
+    , fromFirstOrder
+    , fromLiteral
+    ) where
+
+
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (apply, Apply(..), apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7)
+import Data.Logic.Classes.Combine (binop, BinOp((:&:), (:<=>:), (:=>:), (:|:)), Combinable, Combination(..), combine)
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Failing (Failing(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set as Set (empty, Set, union)
+import Text.PrettyPrint ((<+>), (<>), Doc, nest, parens, text)
+
+
+-- |The 'FirstOrderFormula' type class.  Minimal implementation:
+-- @for_all, exists, foldFirstOrder, foldTerm, (.=.), pApp0-pApp7, fApp, var@.  The
+-- functional dependencies are necessary here so we can write
+-- functions that don't fix all of the type parameters.  For example,
+-- without them the univquant_free_vars function gives the error @No
+-- instance for (FirstOrderFormula Formula atom V)@ because the
+-- function doesn't mention the Term type.
+class ( Formula formula atom
+      , Combinable formula  -- Basic logic operations
+      , Constants formula
+      , Constants atom
+      , HasFixity atom
+      , Variable v
+      , Pretty atom, Pretty v
+      ) => FirstOrderFormula formula atom v | formula -> atom v where
+    -- | Universal quantification - for all x (formula x)
+    for_all :: v -> formula -> formula
+    -- | Existential quantification - there exists x such that (formula x)
+    exists ::  v -> formula -> formula
+
+    -- | A fold function similar to the one in 'PropositionalFormula'
+    -- but extended to cover both the existing formula types and the
+    -- ones introduced here.  @foldFirstOrder (.~.) quant binOp infixPred pApp@
+    -- is a no op.  The argument order is taken from Logic-TPTP.
+    foldFirstOrder :: (Quant -> v -> formula -> r)
+                   -> (Combination formula -> r)
+                   -> (Bool -> r)
+                   -> (atom -> r)
+                   -> formula
+                   -> r
+
+zipFirstOrder :: FirstOrderFormula formula atom v =>
+                 (Quant -> v -> formula -> Quant -> v -> formula -> Maybe r)
+              -> (Combination formula -> Combination formula -> Maybe r)
+              -> (Bool -> Bool -> Maybe r)
+              -> (atom -> atom -> Maybe r)
+              -> formula -> formula -> Maybe r
+zipFirstOrder qu co tf at fm1 fm2 =
+    foldFirstOrder qu' co' tf' at' fm1
+    where
+      qu' op1 v1 p1 = foldFirstOrder (qu op1 v1 p1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      co' c1 = foldFirstOrder (\ _ _ _ -> Nothing) (co c1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' atom1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at atom1) fm2
+
+-- |The 'Quant' and 'InfixPred' types, like the BinOp type in
+-- 'Data.Logic.Propositional', could be additional parameters to the type
+-- class, but it would add additional complexity with unclear
+-- benefits.
+data Quant = Forall | Exists deriving (Eq,Ord,Show,Read,Data,Typeable,Enum,Bounded)
+
+pApp :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> [term] -> formula
+pApp p ts = atomic (apply p ts :: atom)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a :: atom)
+pApp2 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b :: atom)
+pApp3 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c :: atom)
+pApp4 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d :: atom)
+pApp5 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e :: atom)
+pApp6 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f :: atom)
+pApp7 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g :: atom)
+
+-- |for_all with a list of variables, for backwards compatibility.
+for_all' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+for_all' vs f = foldr for_all f vs
+
+-- |exists with a list of variables, for backwards compatibility.
+exists' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+exists' vs f = foldr for_all f vs
+
+-- |Names for for_all and exists inspired by the conventions of the
+-- TPTP project.
+(!) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(!) = for_all
+(?) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(?) = exists
+
+-- Irrelevant, because these are always used as prefix operators, never as infix.
+infixr 9 !, ?, ∀, ∃
+
+-- | ∀ can't be a function when -XUnicodeSyntax is enabled.
+(∀) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∀) = for_all
+(∃) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∃) = exists
+
+-- | Helper function for building folds.
+quant :: FirstOrderFormula formula atom v => 
+         Quant -> v -> formula -> formula
+quant Forall v f = for_all v f
+quant Exists v f = exists v f
+
+-- |Legacy version of quant from when we supported lists of quantified
+-- variables.  It also has the virtue of eliding quantifications with
+-- empty variable lists (by calling for_all' and exists'.)
+quant' :: FirstOrderFormula formula atom v => 
+         Quant -> [v] -> formula -> formula
+quant' Forall = for_all'
+quant' Exists = exists'
+
+convertFOF :: (FirstOrderFormula formula1 atom1 v1, FirstOrderFormula formula2 atom2 v2) =>
+              (atom1 -> atom2) -> (v1 -> v2) -> formula1 -> formula2
+convertFOF convertA convertV formula =
+    foldFirstOrder qu co tf (atomic . convertA) formula
+    where
+      convert' = convertFOF convertA convertV
+      qu x v f = quant x (convertV v) (convert' f)
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+
+-- |Try to convert a first order logic formula to propositional.  This
+-- will return Nothing if there are any quantifiers, or if it runs
+-- into an atom that it is unable to convert.
+toPropositional :: forall formula1 atom v formula2 atom2.
+                   (FirstOrderFormula formula1 atom v,
+                    P.PropositionalFormula formula2 atom2) =>
+                   (atom -> atom2) -> formula1 -> formula2
+toPropositional convertAtom formula =
+    foldFirstOrder qu co tf at formula
+    where
+      convert' = toPropositional convertAtom
+      qu _ _ _ = error "toPropositional: invalid argument"
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+      at = atomic . convertAtom
+
+-- | Display a formula in a format that can be read into the interpreter.
+showFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v, Show v) => (atom -> String) -> formula -> String
+showFirstOrder sa formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu Forall v f = "(for_all " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
+      qu Exists v f = "(exists " ++  show v ++ " " ++ showFirstOrder sa f ++ ")"
+      co (BinOp f1 op f2) = "(" ++ parenForm f1 ++ " " ++ showCombine op ++ " " ++ parenForm f2 ++ ")"
+      co ((:~:) f) = "((.~.) " ++ showFirstOrder sa f ++ ")"
+      tf x = if x then "true" else "false"
+      at :: atom -> String
+      at = sa
+      parenForm x = "(" ++ showFirstOrder sa x ++ ")"
+      showCombine (:<=>:) = ".<=>."
+      showCombine (:=>:) = ".=>."
+      showCombine (:&:) = ".&."
+      showCombine (:|:) = ".|."
+
+prettyFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v) =>
+                      (Int -> atom -> Doc) -> (v -> Doc) -> Int -> formula -> Doc
+prettyFirstOrder pa pv pprec formula =
+    parensIf (pprec > prec) $
+    foldFirstOrder
+          (\ qop v f -> prettyQuant qop <> pv v <> text "." <+> (prettyFirstOrder pa pv prec f))
+          (\ cm ->
+               case cm of
+                 (BinOp f1 op f2) ->
+                     case op of
+                       (:=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:<=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:&:) -> (prettyFirstOrder pa pv 3 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:|:) -> (prettyFirstOrder pa pv 4 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                 ((:~:) f) -> text "¬" {-"~"-} <> prettyFirstOrder pa pv prec f)
+          (text . ifElse "true" "false")
+          (pa prec)
+          formula
+    where
+      Fixity prec _ = fixityFirstOrder formula
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      prettyQuant Forall = text "∀" -- "!"
+      prettyQuant Exists = text "∃" -- "?"
+
+fixityFirstOrder :: (HasFixity atom, FirstOrderFormula formula atom v) => formula -> Fixity
+fixityFirstOrder formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu _ _ _ = Fixity 10 InfixN
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- | Examine the formula to find the list of outermost universally
+-- quantified variables, and call a function with that list and the
+-- formula after the quantifiers are removed.
+withUnivQuants :: FirstOrderFormula formula atom v => ([v] -> formula -> r) -> formula -> r
+withUnivQuants fn formula =
+    doFormula [] formula
+    where
+      doFormula vs f =
+          foldFirstOrder
+                (doQuant vs)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                f
+      doQuant vs Forall v f = doFormula (v : vs) f
+      doQuant vs Exists v f = fn (reverse vs) (exists v f)
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+mapAtomsFirstOrder :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+mapAtomsFirstOrder f fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op v p = quant op v (mapAtomsFirstOrder f p)
+      co ((:~:) p) = mapAtomsFirstOrder f p
+      co (BinOp p op q) = binop (mapAtomsFirstOrder f p) op (mapAtomsFirstOrder f q)
+      tf flag = fromBool flag
+      at x = f x
+
+-- | Deprecated - use mapAtoms
+onatoms :: forall formula atom v. (FirstOrderFormula formula atom v) => (atom -> formula) -> formula -> formula
+onatoms = mapAtomsFirstOrder
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- -------------------------------------------------------------------------
+
+foldAtomsFirstOrder :: FirstOrderFormula fof atom v => (r -> atom -> r) -> r -> fof -> r
+foldAtomsFirstOrder f i fof =
+        foldFirstOrder qu co (const i) (f i) fof
+        where
+          qu _ _ fof' = foldAtomsFirstOrder f i fof'
+          co ((:~:) fof') = foldAtomsFirstOrder f i fof'
+          co (BinOp p _ q) = foldAtomsFirstOrder f (foldAtomsFirstOrder f i q) p
+
+-- | Deprecated - use foldAtoms
+overatoms :: forall formula atom v r. FirstOrderFormula formula atom v =>
+             (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsFirstOrder (flip f) b fm
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: forall formula atom v a. (FirstOrderFormula formula atom v, Ord a) =>
+              (atom -> Set.Set a) -> formula -> Set.Set a
+atom_union f fm = overatoms (\ h t -> Set.union (f h) t) fm Set.empty
+
+$(deriveSafeCopy 1 'base ''Quant)
+
+
+{- This makes bad things happen.
+-- | We can use an fof type as a lit, but it must not use some constructs.
+instance FirstOrderFormula fof atom v => Literal fof atom v where
+    foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm
+        where qu = error "instance Literal FirstOrderFormula"
+              co ((:~:) x) = neg x
+              co _ = error "instance Literal FirstOrderFormula"
+    atomic = Data.Logic.Classes.FirstOrder.atomic
+-}
+
+-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
+-- with a construct unsupported in a normal logic formula,
+-- i.e. quantifiers and formula combinators other than negation.
+ 
+fromFirstOrder ::
+               forall formula atom v lit atom2 .
+                   (Formula lit atom2,
+                    Data.Logic.Classes.FirstOrder.FirstOrderFormula formula atom v,
+                    Literal lit atom2) =>
+                   (atom -> atom2) -> formula -> Failing lit
+fromFirstOrder ca formula
+  = Data.Logic.Classes.FirstOrder.foldFirstOrder
+      (\ _ _ _ -> Failure ["fromFirstOrder"])
+      co
+      (Success . fromBool)
+      (Success . atomic . ca)
+      formula
+  where  
+        co :: Combination formula -> Failing lit
+        co ((:~:) f) = fromFirstOrder ca f >>= return . (.~.)
+        co _ = Failure ["fromFirstOrder"]
+
+
+ 
+fromLiteral ::
+            forall lit atom v fof atom2 .
+                (Literal lit atom,
+                 Data.Logic.Classes.FirstOrder.FirstOrderFormula fof atom2 v) =>
+                (atom -> atom2) -> lit -> fof
+fromLiteral ca lit = foldLiteral (\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit
+
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Formula.hs b/testdata/split-merge-expected/Data/Logic/Classes/Formula.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Formula.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Data.Logic.Classes.Formula
+    ( Formula(atomic, foldAtoms, mapAtoms)
+    ) where
+
+class Formula formula atom where
+    atomic :: atom -> formula
+    foldAtoms :: Formula formula atom => (r -> atom -> r) -> r -> formula -> r
+    mapAtoms :: Formula formula atom => (atom -> formula) -> formula -> formula
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/FoldAtomsLiteral.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.FoldAtomsLiteral
+    ( foldAtomsLiteral
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+
+foldAtomsLiteral :: Literal lit atom => (r -> atom -> r) -> r -> lit -> r
+foldAtomsLiteral f i lit = foldLiteral (foldAtomsLiteral f i) (const i) (f i) lit
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/Internal/FixityLiteral.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.Internal.FixityLiteral
+    ( fixityLiteral
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..))
+
+fixityLiteral :: (Literal formula atom) => formula -> Fixity
+fixityLiteral formula =
+    foldLiteral neg tf at formula
+    where
+      neg _ = Fixity 5 InfixN
+      tf _ = Fixity 10 InfixN
+      at = fixity
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/Literal.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/Literal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/Literal.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.Literal
+    ( Literal(foldLiteral)
+    ) where
+
+import Data.Logic.Classes.Constants (Constants)
+import Data.Logic.Classes.Formula (Formula)
+import Data.Logic.Classes.Negate (Negatable)
+import Data.Logic.Classes.Pretty (HasFixity)
+
+-- |Literals are the building blocks of the clause and implicative normal
+-- |forms.  They support negation and must include True and False elements.
+class (Negatable lit, Constants lit, HasFixity atom, Formula lit atom, Ord lit) => Literal lit atom | lit -> atom where
+    foldLiteral :: (lit -> r) -> (Bool -> r) -> (atom -> r) -> lit -> r
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/PrettyLit.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/PrettyLit.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/PrettyLit.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.PrettyLit
+    ( prettyLit
+    ) where
+
+import Data.Logic.Classes.Literal.Internal.FixityLiteral (fixityLiteral)
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate (negated)
+import Data.Logic.Classes.Pretty (Fixity(Fixity))
+import Text.PrettyPrint ((<>), Doc, nest, parens, text)
+
+{-
+prettyLit :: forall lit atom term v p f. (Literal lit atom v, Apply atom p term, Term term v f) =>
+              (v -> Doc)
+           -> (p -> Doc)
+           -> (f -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pv pp pf _prec lit =
+    foldLiteral neg tf at lit
+    where
+      neg :: lit -> Doc
+      neg x = if negated x then text {-"¬"-} "~" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x
+      tf = text . ifElse "true" "false"
+      at = foldApply (\ pr ts -> 
+                        pp pr <> case ts of
+                                   [] -> empty
+                                   _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+                   (\ x -> text $ if x then "true" else "false")
+      -- parensIf False = id
+      -- parensIf _ = parens . nest 1
+-}
+
+prettyLit :: forall lit atom v. (Literal lit atom) =>
+              (Int -> atom -> Doc)
+           -> (v -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pa pv pprec lit =
+    parensIf (pprec > prec) $ foldLiteral co tf at lit
+    where
+      co :: lit -> Doc
+      co x = if negated x then text {-"¬"-} "~" <> prettyLit pa pv 5 x else prettyLit pa pv 5 x
+      tf x = text (if x then "true" else "false")
+      at = pa 6
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      Fixity prec _ = fixityLiteral lit
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/ToPropositional.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/ToPropositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/ToPropositional.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.ToPropositional
+    ( toPropositional
+    ) where
+
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
+
+toPropositional :: forall lit atom pf atom2. (Literal lit atom, P.PropositionalFormula pf atom2) =>
+                   (atom -> atom2) -> lit -> pf
+toPropositional ca lit = foldLiteral (\ p -> (.~.) (toPropositional ca p)) fromBool (atomic . ca) lit
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Literal/ZipLiterals.hs b/testdata/split-merge-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Literal/ZipLiterals.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal.ZipLiterals
+    ( zipLiterals
+    ) where
+
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+
+zipLiterals :: Literal lit atom =>
+               (lit -> lit -> Maybe r)
+            -> (Bool -> Bool -> Maybe r)
+            -> (atom -> atom -> Maybe r)
+            -> lit -> lit -> Maybe r
+zipLiterals neg tf at fm1 fm2 =
+    foldLiteral neg' tf' at' fm1
+    where
+      neg' p1 = foldLiteral (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldLiteral (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' a1 = foldLiteral (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Negate.hs b/testdata/split-merge-expected/Data/Logic/Classes/Negate.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Negate.hs
@@ -0,0 +1,42 @@
+module Data.Logic.Classes.Negate
+     ( Negatable(..)
+     , negated
+     , (.~.)
+     , (¬)
+     , negative
+     , positive
+     ) where
+
+-- |The class of formulas that can be negated.  There are some types
+-- that can be negated but do not support the other Boolean Logic
+-- operators, such as the 'Literal' class.
+class Negatable formula where
+    -- | Negate a formula in a naive fashion, the operators below
+    -- prevent double negation.
+    negatePrivate :: formula -> formula
+    -- | Test whether a formula is negated or normal
+    foldNegation :: (formula -> r) -- ^ called for normal formulas
+                 -> (formula -> r) -- ^ called for negated formulas
+                 -> formula -> r
+-- | Is this formula negated at the top level?
+negated :: Negatable formula => formula -> Bool
+negated = foldNegation (const False) (not . negated)
+
+-- | Negate the formula, avoiding double negation
+(.~.) :: Negatable formula => formula -> formula
+(.~.) = foldNegation negatePrivate id
+
+(¬) :: Negatable formula => formula -> formula
+(¬) = (.~.)
+
+infix 5 .~., ¬
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.  (These names are used in Harrison's code.)
+-- ------------------------------------------------------------------------- 
+
+negative :: Negatable formula => formula -> Bool
+negative = negated
+
+positive :: Negatable formula => formula -> Bool
+positive = not . negative
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Pretty.hs b/testdata/split-merge-expected/Data/Logic/Classes/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Pretty.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Classes.Pretty
+    ( Pretty(pretty)
+    , HasFixity(fixity)
+    , TH.Fixity(..)
+    , TH.FixityDirection(..)
+    , topFixity
+    , botFixity
+    ) where
+
+import qualified Language.Haskell.TH.Syntax as TH
+import Text.PrettyPrint (Doc, text)
+
+-- | The intent of this class is to be similar to Show, but only one
+-- way, with no corresponding Read class.  It doesn't really belong
+-- here in logic-classes.  To put something in a pretty printing class
+-- implies that there is only one way to pretty print it, which is not
+-- an assumption made by Text.PrettyPrint.  But in practice this is
+-- often good enough.
+class Pretty x where
+    pretty :: x -> Doc
+
+-- | A class used to do proper parenthesization of formulas.  If we
+-- nest a higher precedence formula inside a lower one parentheses can
+-- be omitted.  Because @|@ has lower precedence than @&@, the formula
+-- @a | (b & c)@ appears as @a | b & c@, while @(a | b) & c@ appears
+-- unchanged.  (Name Precedence chosen because Fixity was taken.)
+-- 
+-- The second field of Fixity is the FixityDirection, which can be
+-- left, right, or non.  A left associative operator like @/@ is
+-- grouped left to right, so parenthese can be omitted from @(a / b) /
+-- c@ but not from @a / (b / c)@.  It is a syntax error to omit
+-- parentheses when formatting a non-associative operator.
+-- 
+-- The Haskell FixityDirection type is concerned with how to interpret
+-- a formula formatted in a certain way, but here we are concerned
+-- with how to format a formula given its interpretation.  As such,
+-- one case the Haskell type does not capture is whether the operator
+-- follows the associative law, so we can omit parentheses in an
+-- expression such as @a & b & c@.
+class HasFixity x where
+    fixity :: x -> TH.Fixity
+
+-- Definitions from template-haskell:
+-- data Fixity = Fixity Int FixityDirection
+-- data FixityDirection = InfixL | InfixR | InfixN
+
+-- | This is used as the initial value for the parent fixity.
+topFixity :: TH.Fixity
+topFixity = TH.Fixity 0 TH.InfixN
+
+-- | This is used as the fixity for things that never need
+-- parenthesization, such as function application.
+botFixity :: TH.Fixity
+botFixity = TH.Fixity 10 TH.InfixN
+
+instance Pretty String where
+    pretty = text
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Propositional.hs b/testdata/split-merge-expected/Data/Logic/Classes/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Propositional.hs
@@ -0,0 +1,333 @@
+-- | PropositionalFormula is a multi-parameter type class for
+-- representing instance of propositional (aka zeroth order) logic
+-- datatypes.  These are formulas which have truth values, but no "for
+-- all" or "there exists" quantifiers and thus no variables or terms
+-- as we have in first order or predicate logic.  It is intended that
+-- we will be able to write instances for various different
+-- implementations to allow these systems to interoperate.  The
+-- operator names were adopted from the Logic-TPTP package.
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Classes.Propositional
+    ( PropositionalFormula(..)
+    , showPropositional
+    , prettyPropositional
+    , fixityPropositional
+    , convertProp
+    , combine
+    , negationNormalForm
+    , clauseNormalForm
+    , clauseNormalForm'
+    , clauseNormalFormAlt
+    , clauseNormalFormAlt'
+    , disjunctiveNormalForm
+    , disjunctiveNormalForm'
+    , overatoms
+    , foldAtomsPropositional
+    , mapAtomsPropositional
+    ) where
+
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool, prettyBool)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate
+import Data.Logic.Classes.Pretty (Pretty, HasFixity(fixity), Fixity(Fixity), FixityDirection(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as Set
+import Text.PrettyPrint (Doc, text, (<>))
+
+-- |A type class for propositional logic.  If the type we are writing
+-- an instance for is a zero-order (aka propositional) logic type
+-- there will generally by a type or a type parameter corresponding to
+-- atom.  For first order or predicate logic types, it is generally
+-- easiest to just use the formula type itself as the atom type, and
+-- raise errors in the implementation if a non-atomic formula somehow
+-- appears where an atomic formula is expected (i.e. as an argument to
+-- atomic or to the third argument of foldPropositional.)
+-- 
+-- The Ord superclass is required so we can put formulas in sets
+-- during the normal form computations.  Negatable and Combinable are
+-- also considered basic operations that we can't build this package
+-- without.  It is less obvious whether Constants is always required,
+-- but the implementation of functions like simplify would be more
+-- elaborate if we didn't have it, so we will require it.
+class (Ord formula, Negatable formula, Combinable formula, Constants formula,
+       Pretty formula, HasFixity formula, Formula formula atom) => PropositionalFormula formula atom | formula -> atom where
+    -- | Build an atomic formula from the atom type.
+    -- | A fold function that distributes different sorts of formula
+    -- to its parameter functions, one to handle binary operators, one
+    -- for negations, and one for atomic formulas.  See examples of its
+    -- use to implement the polymorphic functions below.
+    foldPropositional :: (Combination formula -> r)
+                      -> (Bool -> r)
+                      -> (atom -> r)
+                      -> formula -> r
+
+-- | Show a formula in a format that can be evaluated 
+showPropositional :: (PropositionalFormula formula atom) => (atom -> String) -> formula -> String
+showPropositional showAtom formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) f) = "(.~.) " ++ parenForm f
+      co (BinOp f1 op f2) = parenForm f1 ++ " " ++ showFormOp op ++ " " ++ parenForm f2
+      tf True = "true"
+      tf False = "false"
+      at = showAtom
+      parenForm x = "(" ++ showPropositional showAtom x ++ ")"
+      showFormOp (:<=>:) = ".<=>."
+      showFormOp (:=>:) = ".=>."
+      showFormOp (:&:) = ".&."
+      showFormOp (:|:) = ".|."
+
+-- | Show a formula in a visually pleasing format.
+prettyPropositional :: (PropositionalFormula formula atom, HasFixity formula) =>
+                       (atom -> Doc)
+                    -> Fixity        -- ^ The fixity of the parent formula.  If the operator being formatted here
+                                     -- has a lower precedence it needs to be parenthesized.
+                    -> formula
+                    -> Doc
+prettyPropositional prettyAtom (Fixity pprec _pdir) formula =
+    parenIf (pprec > prec) (foldPropositional co tf at formula)
+    where
+      co ((:~:) f) = text "¬" <> prettyPropositional prettyAtom fix f
+      co (BinOp f1 op f2) = prettyPropositional prettyAtom fix f1 <> text " " <> prettyBinOp op <> text " " <> prettyPropositional prettyAtom fix f2
+      tf = prettyBool
+      at = prettyAtom
+      -- parenForm x = cat [text "(", prettyPropositional prettyAtom 0 x, text ")"]
+      parenIf True x = text "(" <> x <> text ")"
+      parenIf False x = x
+      fix@(Fixity prec _dir) = fixity formula
+
+fixityPropositional :: (HasFixity atom, PropositionalFormula formula atom) => formula -> Fixity
+fixityPropositional formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- |Convert any instance of a propositional logic expression to any
+-- other using the supplied atom conversion function.
+convertProp :: forall formula1 atom1 formula2 atom2.
+               (PropositionalFormula formula1 atom1,
+                PropositionalFormula formula2 atom2) =>
+               (atom1 -> atom2) -> formula1 -> formula2
+convertProp convertA formula =
+    foldPropositional c fromBool a formula
+    where
+      convert' = convertProp convertA
+      c ((:~:) f) = (.~.) (convert' f)
+      c (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      a = atomic . convertA
+
+-- | Simplify and recursively apply nnf.
+negationNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+negationNormalForm = nnf . psimplify
+
+-- |Eliminate => and <=> and move negations inwards:
+-- 
+-- @
+-- Formula      Rewrites to
+--  P => Q      ~P | Q
+--  P <=> Q     (P & Q) | (~P & ~Q)
+-- ~∀X P        ∃X ~P
+-- ~∃X P        ∀X ~P
+-- ~(P & Q)     (~P | ~Q)
+-- ~(P | Q)     (~P & ~Q)
+-- ~~P  P
+-- @
+-- 
+nnf :: (PropositionalFormula formula atom) => formula -> formula
+nnf fm = foldPropositional (nnfCombine fm) fromBool (\ _ -> fm) fm
+
+nnfCombine :: (PropositionalFormula formula atom) => formula -> Combination formula -> formula
+nnfCombine fm ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+nnfCombine _ (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+nnfCombine _ (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+nnfCombine _ (BinOp p (:&:) q) = nnf p .&. nnf q
+nnfCombine _ (BinOp p (:|:) q) = nnf p .|. nnf q
+
+nnfNotCombine :: (PropositionalFormula formula atom) => Combination formula -> formula
+nnfNotCombine ((:~:) p) = nnf p
+nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- |Do a bottom-up recursion to simplify a propositional formula.
+psimplify :: (PropositionalFormula formula atom) => formula -> formula
+psimplify fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      co (BinOp p (:&:) q) = psimplify1 (psimplify p .&. psimplify q)
+      co (BinOp p (:|:) q) = psimplify1 (psimplify p .|. psimplify q)
+      co (BinOp p (:=>:) q) = psimplify1 (psimplify p .=>. psimplify q)
+      co (BinOp p (:<=>:) q) = psimplify1 (psimplify p .<=>. psimplify q)
+      tf _ = fm
+      at _ = fm
+
+-- |Do one step of simplify for propositional formulas:
+-- Perform the following transformations everywhere, plus any
+-- commuted versions for &, |, and <=>.
+-- 
+-- @
+--  ~False      -> True
+--  ~True       -> False
+--  True & P    -> P
+--  False & P   -> False
+--  True | P    -> True
+--  False | P   -> P
+--  True => P   -> P
+--  False => P  -> True
+--  P => True   -> P
+--  P => False  -> True
+--  True <=> P  -> P
+--  False <=> P -> ~P
+-- @
+-- 
+psimplify1 :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) f) = foldPropositional simplifyNotCombine (fromBool . not) simplifyNotAtom f
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _)            -> r
+            (Just False, (:&:), _)            -> fromBool False
+            (_,          (:&:), Just True)    -> l
+            (_,          (:&:), Just False)   -> fromBool False
+            (Just True,  (:|:), _)            -> fromBool True
+            (Just False, (:|:), _)            -> r
+            (_,          (:|:), Just True)    -> fromBool True
+            (_,          (:|:), Just False)   -> l
+            (Just True,  (:=>:), _)           -> r
+            (Just False, (:=>:), _)           -> fromBool True
+            (_,          (:=>:), Just True)   -> fromBool True
+            (_,          (:=>:), Just False)  -> (.~.) l
+            (Just False, (:<=>:), Just False) -> fromBool True
+            (Just True,  (:<=>:), _)          -> r
+            (Just False, (:<=>:), _)          -> (.~.) r
+            (_,          (:<=>:), Just True)  -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _                                 -> fm
+      simplifyNotCombine ((:~:) f) = f
+      simplifyNotCombine _ = fm
+      simplifyNotAtom x = (.~.) (atomic x)
+
+clauseNormalForm' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalForm' = simp purecnf . negationNormalForm
+
+clauseNormalForm :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalForm formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalForm' formula
+
+-- |I'm not sure of the clauseNormalForm functions above are wrong or just different.
+clauseNormalFormAlt' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalFormAlt' = simp purecnf' . negationNormalForm
+
+clauseNormalFormAlt :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalFormAlt formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalFormAlt' formula
+
+disjunctiveNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+disjunctiveNormalForm formula =
+    case clean (lists dnf) of
+      [] -> fromBool False
+      xss -> foldr1 (.|.) . map (foldr1 (.&.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      dnf = disjunctiveNormalForm' formula
+
+disjunctiveNormalForm' :: (PropositionalFormula formula atom, Eq formula) => formula -> Set.Set (Set.Set formula)
+disjunctiveNormalForm' = simp purednf . negationNormalForm
+
+simp :: forall formula atom. (PropositionalFormula formula atom) =>
+        (formula -> Set.Set (Set.Set formula)) -> formula -> Set.Set (Set.Set formula)
+simp purenf fm =
+    case (compare fm (fromBool False), compare fm (fromBool True)) of
+      (EQ, _) -> Set.empty
+      (_, EQ) -> Set.singleton Set.empty
+      _ ->cjs'
+    where
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (Set.isProperSubsetOf x) cjs))
+      cjs = Set.filter (not . trivial) (purenf (nnf fm)) :: Set.Set (Set.Set formula)
+
+-- |Harrison page 59.  Look for complementary pairs in a clause.
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection (Set.map (.~.) n) p
+    where (n, p) = Set.partition negated lits
+
+purecnf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+purednf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purednf fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.distrib (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+purecnf' :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf' fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.union (purecnf' p) (purecnf' q)
+      c (BinOp p (:|:) q) = Set.distrib (purecnf' p) (purecnf' q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+-- | Use this to implement foldAtoms
+foldAtomsPropositional :: PropositionalFormula pf atom => (r -> atom -> r) -> r -> pf -> r
+foldAtomsPropositional f i pf =
+        foldPropositional co (const i) (f i) pf
+        where
+          co ((:~:) pf') = foldAtomsPropositional f i pf'
+          co (BinOp p _ q) = foldAtomsPropositional f (foldAtomsPropositional f i q) p
+
+-- | Deprecated - use foldAtoms.
+overatoms :: forall formula atom r. PropositionalFormula formula atom => (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsPropositional (flip f) b fm
+
+mapAtomsPropositional :: forall formula atom. PropositionalFormula formula atom => (atom -> formula) -> formula -> formula
+mapAtomsPropositional f fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = mapAtomsPropositional f p
+      co (BinOp p op q) = binop (mapAtomsPropositional f p) op (mapAtomsPropositional f q)
+      tf flag = fromBool flag
+      at x = f x
+
+$(deriveSafeCopy 1 'base ''BinOp)
+$(deriveSafeCopy 1 'base ''Combination)
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Skolem.hs b/testdata/split-merge-expected/Data/Logic/Classes/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Skolem.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.Skolem where
+
+import Data.Logic.Classes.Variable (Variable)
+
+-- |This class shows how to convert between atomic Skolem functions
+-- and Ints.  We include a variable type as a parameter because we
+-- create skolem functions to replace an existentially quantified
+-- variable, and it can be helpful to retain a reference to the
+-- variable.
+class Variable v => Skolem f v | f -> v where
+    toSkolem :: v -> f
+    -- ^ Built a Skolem function from the given variable and number.
+    -- The number is generally obtained from the skolem monad.
+    isSkolem  :: f -> Bool
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Term.hs b/testdata/split-merge-expected/Data/Logic/Classes/Term.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Term.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Classes.Term
+    ( Term(..)
+    , Function
+    , convertTerm
+    , showTerm
+    , prettyTerm
+    , fvt
+    , tsubst
+    , funcs
+    ) where
+
+import Data.Generics (Data)
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Skolem
+import Data.Logic.Classes.Variable
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), brackets, hcat, text)
+
+class (Eq f, Ord f, Skolem f v, Data f, Pretty f) => Function f v
+
+class ( Ord term  -- For implementing Ord in Literal
+      , Variable v
+      , Function f v ) => Term term v f | term -> v f where
+    vt :: v -> term
+    -- ^ Build a term which is a variable reference.
+    fApp :: f -> [term] -> term
+    -- ^ Build a term by applying terms to an atomic function.  @f@
+    -- (atomic function) is one of the type parameters, this package
+    -- is mostly indifferent to its internal structure.
+    foldTerm :: (v -> r) -> (f -> [term] -> r) -> term -> r
+    -- ^ A fold for the term data type, which understands terms built
+    -- from a variable and a term built from the application of a
+    -- primitive function to other terms.
+    zipTerms :: (v -> v -> Maybe r) -> (f -> [term] -> f -> [term] -> Maybe r) -> term -> term -> Maybe r
+
+convertTerm :: forall term1 v1 f1 term2 v2 f2.
+               (Term term1 v1 f1,
+                Term term2 v2 f2) =>
+               (v1 -> v2) -> (f1 -> f2) -> term1 -> term2
+convertTerm convertV convertF term =
+    foldTerm v fn term
+    where
+      convertTerm' = convertTerm convertV convertF
+      v = vt . convertV
+      fn x ts = fApp (convertF x) (map convertTerm' ts)
+
+showTerm :: forall term v f. (Term term v f, Show v, Show f) =>
+            term -> String
+showTerm term =
+    foldTerm v f term
+    where
+      v :: v -> String
+      v v' = "vt (" ++ show v' ++ ")"
+      f :: f -> [term] -> String
+      f fn ts = "fApp (" ++ show fn ++ ") [" ++ intercalate "," (map showTerm ts) ++ "]"
+
+prettyTerm :: forall v f term. (Term term v f) =>
+              (v -> Doc)
+           -> (f -> Doc)
+           -> term
+           -> Doc
+prettyTerm pv pf t = foldTerm pv (\ fn ts -> pf fn <> brackets (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts)))) t
+
+fvt :: (Term term v f, Ord v) => term -> Set.Set v
+fvt tm = foldTerm Set.singleton (\ _ args -> Set.unions (map fvt args)) tm
+
+-- ------------------------------------------------------------------------- 
+-- Substitution within terms.                                                
+-- ------------------------------------------------------------------------- 
+
+tsubst :: (Term term v f, Ord v) => Map.Map v term -> term -> term
+tsubst sfn tm = foldTerm (\ x -> fromMaybe tm (Map.lookup x sfn)) (\ fn args -> fApp fn (map (tsubst sfn) args)) tm
+
+funcs :: (Term term v f, Ord f) => term -> Set.Set (f, Int)
+funcs tm =
+    foldTerm (const Set.empty)
+             (\ f args -> foldr (\ arg r -> Set.union (funcs arg) r) (Set.singleton (f, length args)) args)
+             tm
diff --git a/testdata/split-merge-expected/Data/Logic/Classes/Variable.hs b/testdata/split-merge-expected/Data/Logic/Classes/Variable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Classes/Variable.hs
@@ -0,0 +1,32 @@
+module Data.Logic.Classes.Variable
+    ( Variable(..)
+    , variants
+    , showVariable
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Pretty (Pretty)
+import qualified Data.Set as Set
+import Data.String (IsString)
+import Text.PrettyPrint (Doc)
+
+class (Ord v, IsString v, Data v, Pretty v) => Variable v where
+    variant :: v -> Set.Set v -> v
+    -- ^ Return a variable based on v but different from any set
+    -- element.  The result may be v itself if v is not a member of
+    -- the set.
+    prefix :: String -> v -> v
+    -- ^ Modify a variable by adding a prefix.  This unfortunately
+    -- assumes that v is "string-like" but at least one algorithm in
+    -- Harrison currently requires this.
+    prettyVariable :: v -> Doc
+    -- ^ Pretty print a variable
+
+-- | Return an infinite list of variations on v
+variants :: Variable v => v -> [v]
+variants v0 =
+    iter' Set.empty v0
+    where iter' s v = let v' = variant v s in v' : iter' (Set.insert v s) v'
+
+showVariable :: Variable v => v -> String
+showVariable v = "fromString (" ++ show (show (prettyVariable v)) ++ ")"
diff --git a/testdata/split-merge-expected/Data/Logic/Failing.hs b/testdata/split-merge-expected/Data/Logic/Failing.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Failing.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE  DeriveDataTypeable, StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Failing
+    ( Failing(Success, Failure)
+    , failing
+    ) where
+
+import Control.Applicative.Error
+import Data.Generics
+
+failing :: ([String] -> b) -> (a -> b) -> Failing a -> b
+failing f _ (Failure errs) = f errs
+failing _ f (Success a)    = f a
+
+instance Monad Failing where
+  return = Success
+  m >>= f =
+      case m of
+        (Failure errs) -> (Failure errs)
+        (Success a) -> f a
+  fail errMsg = Failure [errMsg]
+
+deriving instance Typeable1 Failing
+deriving instance Data a => Data (Failing a)
+deriving instance Read a => Read (Failing a)
+deriving instance Eq a => Eq (Failing a)
+deriving instance Ord a => Ord (Failing a)
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/DP.hs b/testdata/split-merge-expected/Data/Logic/Harrison/DP.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/DP.hs
@@ -0,0 +1,274 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DP
+    ( tests
+    , dpll
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.DefCNF (defcnfs, NumAtom(..))
+import Data.Logic.Harrison.Lib (allpairs, defined, maximize', minimize', setmapfilter, (|->))
+import Data.Logic.Harrison.Prop (negative, positive, trivial)
+import Data.Logic.Harrison.PropExamples (Atom(..), N, prime)
+import Data.Logic.Tests.HUnit (assertEqual, convert, Test(TestCase, TestList))
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (delete, difference, empty, filter, findMin, flatten, fold, insert, intersection, map, member, minView, null, partition, Set, singleton, size, union)
+
+instance NumAtom (Atom N) where
+    ma n = P "p" n Nothing
+    ai (P _ n _) = n
+
+tests = convert (TestList [test01, test02, test03])
+
+-- ========================================================================= 
+-- The Davis-Putnam and Davis-Putnam-Loveland-Logemann procedures.           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- The DP procedure.                                                         
+-- ------------------------------------------------------------------------- 
+
+one_literal_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+one_literal_rule clauses =
+    case Set.minView (Set.filter (\ cl -> Set.size cl == 1) clauses) of
+      Nothing -> Failure ["one_literal_rule"]
+      Just (s, _) ->
+          let u = Set.findMin s in
+          let u' = (.~.) u in
+          let clauses1 = Set.filter (\ cl -> not (Set.member u cl)) clauses in
+          Success (Set.map (\ cl -> Set.delete u' cl) clauses1)
+
+affirmative_negative_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+affirmative_negative_rule clauses =
+  let (neg',pos) = Set.partition negative (Set.flatten clauses) in
+  let neg = Set.map (.~.) neg' in
+  let pos_only = Set.difference pos neg
+      neg_only = Set.difference neg pos in
+  let pure = Set.union pos_only (Set.map (.~.) neg_only) in
+  if Set.null pure
+  then Failure ["affirmative_negative_rule"]
+  else Success (Set.filter (\ cl -> Set.null (Set.intersection cl pure)) clauses)
+
+resolve_on :: forall lit atom. (Literal lit atom, Ord lit) =>
+              lit -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit)
+resolve_on p clauses =
+  let p' = (.~.) p
+      (pos,notpos) = Set.partition (Set.member p) clauses in
+  let (neg,other) = Set.partition (Set.member p') notpos in
+  let pos' = Set.map (Set.filter (\ l -> l /= p)) pos
+      neg' = Set.map (Set.filter (\ l -> l /= p')) neg in
+  let res0 = allpairs Set.union pos' neg' in
+  Set.union other (Set.filter (not . trivial) res0)
+
+resolution_blowup :: forall formula. (Negatable formula, Ord formula) =>
+                     Set.Set (Set.Set formula) -> formula -> Int
+resolution_blowup cls l =
+  let m = Set.size (Set.filter (Set.member l) cls)
+      n = Set.size (Set.filter (Set.member ((.~.) l)) cls) in
+  m * n - m - n
+
+resolution_rule :: forall lit atom. (Literal lit atom, Ord lit) =>
+                   Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+resolution_rule clauses =
+    let pvs = Set.filter positive (Set.flatten clauses) in
+    case minimize' (resolution_blowup clauses) pvs of
+      Just p -> Success (resolve_on p clauses)
+      Nothing -> Failure ["resolution_rule"]
+
+-- ------------------------------------------------------------------------- 
+-- Overall procedure.                                                        
+-- ------------------------------------------------------------------------- 
+
+dp :: forall lit atom. (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing Bool        
+dp clauses =
+  if Set.null clauses
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dp of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dp of
+                    Success x -> Success x
+                    Failure _ -> resolution_rule clauses >>= dp
+
+-- ------------------------------------------------------------------------- 
+-- Davis-Putnam satisfiability tester and tautology checker.                 
+-- ------------------------------------------------------------------------- 
+
+dpsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dpsat fm = dp (defcnfs fm :: Set.Set (Set.Set pf))
+
+dptaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dptaut fm = dpsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test01 = TestCase (assertEqual "dptaut(prime 11)" (Success True) (dptaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- The same thing but with the DPLL procedure.                               
+-- ------------------------------------------------------------------------- 
+
+posneg_count :: forall formula. (Negatable formula, Ord formula) =>
+                Set.Set (Set.Set formula) -> formula -> Int
+posneg_count cls l =                         
+  let m = Set.size(Set.filter (Set.member l) cls)                 
+      n = Set.size(Set.filter (Set.member ((.~.) l)) cls) in
+  m + n                                  
+
+dpll :: forall lit atom. (Literal lit atom, Ord lit) =>
+        Set.Set (Set.Set lit) -> Failing Bool
+dpll clauses =       
+  if clauses == Set.empty
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dpll of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dpll of
+                    Success x -> Success x
+                    Failure _ ->
+                        let pvs = Set.filter positive (Set.flatten clauses) in
+                        case maximize' (posneg_count clauses) pvs of
+                          Nothing -> Failure ["dpll"]
+                          Just p -> 
+                              case (dpll (Set.insert (Set.singleton p) clauses), dpll (Set.insert (Set.singleton ((.~.) p)) clauses)) of
+                                (Success a, Success b) -> Success (a || b)
+                                (Failure a, Failure b) -> Failure (a ++ b)
+                                (Failure a, _) -> Failure a
+                                (_, Failure b) -> Failure b
+
+dpllsat :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+           pf -> Failing Bool
+dpllsat fm = dpll(defcnfs fm :: Set.Set (Set.Set pf))
+
+dplltaut :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+            pf -> Failing Bool
+dplltaut fm = dpllsat ((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+test02 = TestCase (assertEqual "dplltaut(prime 11)" (Success True) (dplltaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- Iterative implementation with explicit trail instead of recursion.        
+-- ------------------------------------------------------------------------- 
+
+data TrailMix = Guessed | Deduced deriving (Eq, Ord)
+
+unassigned :: forall formula. (Negatable formula, Ord formula) =>
+              Set.Set (Set.Set formula) -> Set.Set (formula, TrailMix) -> Set.Set formula
+unassigned cls trail =
+    Set.difference (Set.flatten (Set.map (Set.map litabs) cls)) (Set.map (litabs . fst) trail)
+    where litabs p = if negated p then (.~.) p else p
+
+unit_subpropagate :: forall formula. (Negatable formula, Ord formula) =>
+                     (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+                  -> (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+unit_subpropagate (cls,fn,trail) =
+  let cls' = Set.map (Set.filter (not . defined fn . (.~.))) cls in
+  let uu cs =
+          case Set.minView cs of
+            Nothing -> Failure ["unit_subpropagate"]
+            Just (c, _) -> if Set.size cs == 1 && not (defined fn c)
+                           then Success cs
+                           else Failure ["unit_subpropagate"] in
+  let newunits = Set.flatten (setmapfilter uu cls') in
+  if Set.null newunits then (cls',fn,trail) else
+  let trail' = Set.fold (\ p t -> Set.insert (p,Deduced) t) trail newunits
+      fn' = Set.fold (\ u -> (u |-> ())) fn newunits in
+  unit_subpropagate (cls',fn',trail')
+
+unit_propagate :: forall t. (Negatable t, Ord t) =>
+                  (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+               -> (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+unit_propagate (cls,trail) =
+  let fn = Set.fold (\ (x,_) -> (x |-> ())) Map.empty trail in
+  let (cls',fn',trail') = unit_subpropagate (cls,fn,trail) in (cls',trail')
+
+backtrack :: forall t. Set.Set (t, TrailMix) -> Set.Set (t, TrailMix)
+backtrack trail =
+  case Set.minView trail of
+    Just ((p,Deduced), tt) -> backtrack tt
+    _ -> trail
+
+dpli :: forall atomic pf. (PropositionalFormula pf atomic, Ord pf) =>
+        Set.Set (Set.Set pf) -> Set.Set (pf, TrailMix) -> Failing Bool
+dpli cls trail =
+  let (cls', trail') = unit_propagate (cls, trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView trail of
+      Just ((p,Guessed), tt) -> dpli cls (Set.insert ((.~.) p, Deduced) tt)
+      _ -> Success False
+  else
+      case unassigned cls (trail' :: Set.Set (pf, TrailMix)) of
+        s | Set.null s -> Success True
+        ps -> case maximize' (posneg_count cls') ps of
+                Just p -> dpli cls (Set.insert (p :: pf, Guessed) trail')
+                Nothing -> Failure ["dpli"]
+
+dplisat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplisat fm = dpli (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplitaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplitaut fm = dplisat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- With simple non-chronological backjumping and learning.                   
+-- ------------------------------------------------------------------------- 
+
+backjump :: forall a. (Negatable a, Ord a) =>
+            Set.Set (Set.Set a) -> a -> Set.Set (a, TrailMix) -> Set.Set (a, TrailMix)
+backjump cls p trail =
+  case Set.minView (backtrack trail) of
+    Just ((q,Guessed), tt) ->
+        let (cls',trail') = unit_propagate (cls, Set.insert (p,Guessed) tt) in
+        if Set.member Set.empty cls' then backjump cls p tt else trail
+    _ -> trail
+
+dplb :: forall a. (Negatable a, Ord a) =>
+        Set.Set (Set.Set a) -> Set.Set (a, TrailMix) -> Failing Bool
+dplb cls trail =
+  let (cls',trail') = unit_propagate (cls,trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView (backtrack trail) of
+      Just ((p,Guessed), tt) ->
+        let trail'' = backjump cls p tt in
+        let declits = Set.filter (\ (_,d) -> d == Guessed) trail'' in
+        let conflict = Set.insert ((.~.) p) (Set.map ((.~.) . fst) declits) in
+        dplb (Set.insert conflict cls) (Set.insert ((.~.) p,Deduced) trail'')
+      _ -> Success False
+  else
+    case unassigned cls trail' of
+      s | Set.null s -> Success True
+      ps -> case maximize' (posneg_count cls') ps of
+              Just p -> dplb cls (Set.insert (p,Guessed) trail')
+              Nothing -> Failure ["dpib"]
+            
+dplbsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplbsat fm = dplb (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplbtaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplbtaut fm = dplbsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test03 = TestList [TestCase (assertEqual "dplitaut(prime 101)" (Success True) (dplitaut(prime 101 :: Formula (Atom N)))),
+                   TestCase (assertEqual "dplbtaut(prime 101)" (Success True) (dplbtaut(prime 101 :: Formula (Atom N))))]
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/DefCNF.hs b/testdata/split-merge-expected/Data/Logic/Harrison/DefCNF.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/DefCNF.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DefCNF
+where
+
+import Data.Logic.Classes.Combine ((.&.), (.<=>.), (.|.), BinOp(..), Combination(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Propositional (overatoms, PropositionalFormula(foldPropositional))
+import Data.Logic.Harrison.Prop (cnf, nenf, simpcnf)
+import Data.Logic.Harrison.PropExamples (N)
+import qualified Data.Map as Map (elems, empty, insert, lookup, Map)
+import qualified Data.Set.Extra as Set (Set, unions)
+
+-- ========================================================================= 
+-- Definitional CNF.                                                         
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+{-
+START_INTERACTIVE;;
+cnf <<p <=> (q <=> r)>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Make a stylized variable and update the index.                            
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P a
+
+class NumAtom atom where
+    ma :: N -> atom
+    ai :: atom -> N
+
+instance NumAtom (Atom N) where
+    ma = P
+    ai (P n) = n
+
+mkprop :: forall pf atom. (PropositionalFormula pf atom, NumAtom atom) => N -> (pf, N)
+mkprop n = (atomic (ma n :: atom), n + 1)
+
+-- ------------------------------------------------------------------------- 
+-- Core definitional CNF procedure.                                          
+-- ------------------------------------------------------------------------- 
+
+maincnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+maincnf trip@(fm, _defs, _n) =
+    foldPropositional co tf at fm
+    where
+      co (BinOp p (:&:) q) = defstep (.&.) (p,q) trip
+      co (BinOp p (:|:) q) = defstep (.|.) (p,q) trip
+      co (BinOp p (:<=>:) q) = defstep (.<=>.) (p,q) trip
+      co (BinOp _ (:=>:) _) = trip
+      co ((:~:) _) = trip
+      tf _ = trip
+      at _ = trip
+
+defstep :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf -> pf -> pf) -> (pf, pf) -> (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+defstep op (p,q) (_fm, defs, n) =
+  let (fm1,defs1,n1) = maincnf (p,defs,n) in
+  let (fm2,defs2,n2) = maincnf (q,defs1,n1) in
+  let fm' = op fm1 fm2 in
+  case Map.lookup fm' defs2 of
+    Just _ -> (fm', defs2, n2)
+    Nothing -> let (v,n3) = mkprop n2 in (v, Map.insert v (v .<=>. fm') defs2,n3)
+
+-- ------------------------------------------------------------------------- 
+-- Make n large enough that "v_m" won't clash with s for any m >= n          
+-- ------------------------------------------------------------------------- 
+
+max_varindex :: NumAtom atom =>  atom -> Int -> Int
+max_varindex atom n = max n (ai atom)
+
+-- ------------------------------------------------------------------------- 
+-- Overall definitional CNF.                                                 
+-- ------------------------------------------------------------------------- 
+
+mk_defcnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) =>
+             ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)) -> pf -> Set.Set (Set.Set lit)
+mk_defcnf fn fm =
+  let fm' = nenf fm in
+  let n = 1 + overatoms max_varindex fm' 0 in
+  let (fm'',defs,_) = fn (fm',Map.empty,n) in
+  let (deflist {- :: [pf]-}) = Map.elems defs in
+  Set.unions (simpcnf fm'' : map simpcnf deflist)
+
+defcnf1 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf1 fm = cnf (mk_defcnf maincnf fm :: Set.Set (Set.Set lit))
+
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf1 <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version tweaked to exploit initial structure.                             
+-- ------------------------------------------------------------------------- 
+
+subcnf :: (PropositionalFormula pf atom, NumAtom atom) =>
+          ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int))
+       -> (pf -> pf -> pf)
+       -> pf
+       -> pf
+       -> (pf, Map.Map pf pf, Int)
+       -> (pf, Map.Map pf pf, Int)
+subcnf sfn op p q (_fm,defs,n) =
+  let (fm1,defs1,n1) = sfn (p,defs,n) in
+  let (fm2,defs2,n2) = sfn (q,defs1,n1) in
+  (op fm1 fm2, defs2, n2)
+
+orcnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+orcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:|:) q) = subcnf orcnf (.|.) p q trip
+      co _ = maincnf trip
+
+andcnf :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> orcnf trip) (\ _ -> orcnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf (.&.) p q trip
+      co _ = orcnf trip
+
+defcnfs :: (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+defcnfs fm = mk_defcnf andcnf fm
+
+defcnf2 :: forall pf lit atom.(PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf2 fm = cnf (defcnfs fm :: Set.Set (Set.Set lit))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version that guarantees 3-CNF.                                            
+-- ------------------------------------------------------------------------- 
+
+andcnf3 :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf3 trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf3 (.&.) p q trip
+      co _ = maincnf trip
+
+defcnf3 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf3 fm = cnf (mk_defcnf andcnf3 fm :: Set.Set (Set.Set lit))
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Equal.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Equal.hs
@@ -0,0 +1,322 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, RankNTypes, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Equal
+where
+
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine ((⇒), (∧))
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Equals ((.=.), applyEq, AtomEq(..), funcsAtomEq, PredicateName(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), (∀))
+import Data.Logic.Classes.Formula (Formula(atomic, foldAtoms))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (atom_union)
+import Data.Logic.Harrison.Lib ((∅))
+import qualified Data.Set as Set (delete, empty, fold, fromList, member, Set, singleton, toList, union)
+import Data.String (IsString(fromString))
+
+-- is_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Bool
+-- is_eq = foldFirstOrder (\ _ _ _ -> False) (\ _ -> False) (\ _ -> False) (foldAtomEq (\ _ _ -> False) (\ _ -> False) (\ _ _ -> True))
+-- 
+-- mk_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+-- mk_eq = (.=.)
+-- 
+-- dest_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing (term, term)
+-- dest_eq fm =
+--     foldFirstOrder (\ _ _ _ -> err) (\ _ -> err) (\ _ -> err) at fm
+--     where
+--       at = foldAtomEq (\ _ _ -> err) (\ _ -> err) (\ s t -> Success (s, t))
+--       err = Failure ["dest_eq: not an equation"]
+-- 
+-- lhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- lhs eq = dest_eq eq >>= return . fst
+-- rhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- rhs eq = dest_eq eq >>= return . snd
+
+-- ------------------------------------------------------------------------- 
+-- The set of predicates in a formula.                                       
+-- ------------------------------------------------------------------------- 
+
+predicates :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) => formula -> Set.Set (PredicateName p)
+predicates fm =
+    atom_union pair fm
+    where -- pair :: atom -> Set.Set (p, Int)
+          pair = foldAtomEq (\ p a -> Set.singleton (Named p (maybe (length a)
+                                                                    (\ n -> if n /= length a then n else error "arity mismatch")
+                                                                    (arity p))))
+                            (\ x -> Set.singleton (Named (fromBool x) 0))
+                            (\ _ _ -> Set.singleton Equals)
+
+{-
+-- | Traverse a formula and pass all (predicates, arity) pairs to a function.
+-- To collect
+foldPredicates :: forall formula atom term v p r. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) =>
+                  (PredicateName p -> Maybe Int -> r -> r) -> formula -> r -> r
+foldPredicates f fm acc =
+    foldFirstOrder qu co tf at fm
+    where
+      fold = foldPredicates f
+      qu _ _ p = fold p acc
+      co (BinOp l _ r) = fold r (fold l acc)
+      co ((:~:) p) = fold p acc
+      tf x = fold (fromBool x) acc
+      at = foldAtomEq ap tf eq
+      ap p _ = f (Name p) (arity p) acc
+      eq _ _ = f Equals (Just 2) acc
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Code to generate equality axioms for functions.                           
+-- ------------------------------------------------------------------------- 
+
+function_congruence :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f) =>
+                       (f, Int) -> Set.Set fof
+function_congruence (_,0) = (∅)
+function_congruence (f,n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x :: [v]
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y :: [v]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = fApp f args_x .=. fApp f args_y
+  
+-- ------------------------------------------------------------------------- 
+-- And for predicates.                                                       
+-- ------------------------------------------------------------------------- 
+
+predicate_congruence :: (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord p) =>
+                        PredicateName p -> Set.Set fof
+predicate_congruence Equals = Set.empty
+predicate_congruence (Named _ 0) = Set.empty
+predicate_congruence (Named p n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = atomic (applyEq p args_x) ⇒ atomic (applyEq p args_y)
+
+-- ------------------------------------------------------------------------- 
+-- Hence implement logic with equality just by adding equality "axioms".     
+-- ------------------------------------------------------------------------- 
+
+equivalence_axioms :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord fof) => Set.Set fof
+equivalence_axioms =
+    Set.fromList
+    [(∀) "x" (x .=. x),
+     (∀) "x" ((∀) "y" ((∀) "z" (x .=. y ∧ x .=. z ⇒ y .=. z)))]
+    where
+      x :: term
+      x = vt (fromString "x")
+      y :: term
+      y = vt (fromString "y")
+      z :: term
+      z = vt (fromString "z")
+
+equalitize :: forall formula atom term v p f. (FirstOrderFormula formula atom v, Formula formula atom, AtomEq atom p term, Ord p, Show p, Term term v f, Ord formula, Ord f) =>
+              formula -> formula
+equalitize fm =
+    if not (Set.member Equals allpreds)
+    then fm
+    else foldr1 (∧) (Set.toList axioms) ⇒ fm
+    where
+      axioms = Set.fold (Set.union . function_congruence) (Set.fold (Set.union . predicate_congruence) equivalence_axioms preds) (functions' funcsAtomEq' fm)
+      funcsAtomEq' :: atom -> Set.Set (f, Int)
+      funcsAtomEq' = funcsAtomEq
+      allpreds = predicates fm
+      preds = Set.delete Equals allpreds
+
+functions' :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions' fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- Other variants not mentioned in book.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+{- ************
+
+(meson ** equalitize)
+ <<(forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x * 1 = x) /\
+   (forall x. x * x = 1)
+   ==> forall x y. x * y  = y * x>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With symmetry at leaves and one-sided congruences (Size = 16, 54659 s).   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x. x = x) /\
+   (forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x y z. =((x * y) * z,x * (y * z))) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x = 1 * x) /\
+   (forall x. i(x) * x = 1) /\
+   (forall x. 1 = i(x) * x) /\
+   (forall x y. x = y ==> i(x) = i(y)) /\
+   (forall x y z. x = y ==> x * z = y * z) /\
+   (forall x y z. x = y ==> z * x = z * y) /\
+   (forall x y z. x = y /\ y = z ==> x = z)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Newer version of stratified equalities.                                   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cchain(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cchain(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall x x' u. x = x' /\ achain(i(x'),u) ==> cchain(i(x),u)) /\
+   (forall x x' y y' u.
+        x = x' /\ y = y' /\ achain(x' * y',u) ==> cchain(x * y,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cong(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cong(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Showing congruence closure.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm = equalitize
+ <<forall c. f(f(f(f(f(c))))) = c /\ f(f(f(c))) = c ==> f(c) = c>>;;
+
+time meson fm;;
+
+let fm =
+ <<axiom(f(f(f(f(f(c))))),c) /\
+   axiom(c,f(f(f(f(f(c)))))) /\
+   axiom(f(f(f(c))),c) /\
+   axiom(c,f(f(f(c)))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t) /\
+   (forall x y. x = y ==> cong(f(x),f(y)))
+   ==> f(c) = c>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- With stratified equalities.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x. eqA (x,x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqT (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y z. eqA (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With transitivity chains...                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y. eqA (w,x) ==> eqC (w * y,x * y)) /\
+   (forall w x y. eqC (w,x) ==> eqC (w * y,x * y)) /\
+   (forall x y z. eqA (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqC (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1) \/ eqC (x * i(x),1)>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Enforce canonicity (proof size = 20).                                     
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eq1(x * (y * z),(x * y) * z)) /\
+   (forall x y z. eq1((x * y) * z,x * (y * z))) /\
+   (forall x. eq1(1 * x,x)) /\
+   (forall x. eq1(x,1 * x)) /\
+   (forall x. eq1(i(x) * x,1)) /\
+   (forall x. eq1(1,i(x) * x)) /\
+   (forall x y z. eq1(x,y) ==> eq1(x * z,y * z)) /\
+   (forall x y z. eq1(x,y) ==> eq1(z * x,z * y)) /\
+   (forall x y z. eq1(x,y) /\ eq2(y,z) ==> eq2(x,z)) /\
+   (forall x y. eq1(x,y) ==> eq2(x,y))
+   ==> forall x. eq2(x,i(x))>>;;
+
+time meson fm;;
+
+***************** -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/FOL.hs b/testdata/split-merge-expected/Data/Logic/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/FOL.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.FOL
+    ( eval
+    , list_disj
+    , list_conj
+    , var
+    , fv
+    -- , fv'
+    , subst
+    -- , subst'
+    , generalize
+    ) where
+
+import Data.Logic.Classes.Apply (Apply(..), apply)
+import Data.Logic.Classes.Atom (Atom(allVariables, substitute))
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants (fromBool), true, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term(vt), fvt)
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (on_atoms)
+import Data.Logic.Harrison.Lib ((|->), setAny)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Prelude hiding (pred)
+
+-- =========================================================================
+-- Basic stuff for first order logic.                                       
+--                                                                          
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.) 
+-- =========================================================================
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: FirstOrderFormula formula atom v => formula -> (atom -> Bool) -> Bool
+eval fm v =
+    foldFirstOrder qu co id at fm
+    where
+      qu _ _ p = eval p v
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at = v
+
+list_conj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (FirstOrderFormula formula atom v, Ord formula) =>
+          Set.Set formula -> (atom -> Bool) -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+-- -------------------------------------------------------------------------
+-- Special case of applying a subfunction to the top *terms*.               
+-- -------------------------------------------------------------------------
+
+on_formula :: forall fol atom term v p. (FirstOrderFormula fol atom v, Apply atom p term) => (term -> term) -> fol -> fol
+on_formula f = on_atoms (foldApply (\ p ts -> atomic (apply p (map f ts) :: atom)) fromBool)
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of terms.                                                         
+-- ------------------------------------------------------------------------- 
+
+{-
+let is_const_name s = forall numeric (explode s) or s = "nil";;
+
+let rec parse_atomic_term vs inp =
+  match inp with
+    [] -> failwith "term expected"
+  | "("::rest -> parse_bracketed (parse_term vs) ")" rest
+  | "-"::rest -> papply (fun t -> Fn("-",[t])) (parse_atomic_term vs rest)
+  | f::"("::")"::rest -> Fn(f,[]),rest
+  | f::"("::rest ->
+      papply (fun args -> Fn(f,args))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | a::rest ->
+      (if is_const_name a & not(mem a vs) then Fn(a,[]) else Var a),rest
+
+and parse_term vs inp =
+  parse_right_infix "::" (fun (e1,e2) -> Fn("::",[e1;e2]))
+    (parse_right_infix "+" (fun (e1,e2) -> Fn("+",[e1;e2]))
+       (parse_left_infix "-" (fun (e1,e2) -> Fn("-",[e1;e2]))
+          (parse_right_infix "*" (fun (e1,e2) -> Fn("*",[e1;e2]))
+             (parse_left_infix "/" (fun (e1,e2) -> Fn("/",[e1;e2]))
+                (parse_left_infix "^" (fun (e1,e2) -> Fn("^",[e1;e2]))
+                   (parse_atomic_term vs)))))) inp;;
+
+let parset = make_parser (parse_term []);;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas.                                                      
+-- ------------------------------------------------------------------------- 
+
+let parse_infix_atom vs inp =       
+  let tm,rest = parse_term vs inp in
+  if exists (nextin rest) ["="; "<"; "<="; ">"; ">="] then                     
+        papply (fun tm' -> Atom(R(hd rest,[tm;tm'])))                          
+               (parse_term vs (tl rest))                                       
+  else failwith "";;
+                                                               
+let parse_atom vs inp =
+  try parse_infix_atom vs inp with Failure _ ->                                
+  match inp with                                                               
+  | p::"("::")"::rest -> Atom(R(p,[])),rest                                    
+  | p::"("::rest ->
+      papply (fun args -> Atom(R(p,args)))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | p::rest when p <> "(" -> Atom(R(p,[])),rest
+  | _ -> failwith "parse_atom";;
+                                                                               
+let parse = make_parser                                                        
+  (parse_formula (parse_infix_atom,parse_atom) []);;              
+
+-- ------------------------------------------------------------------------- 
+-- Set up parsing of quotations.                                             
+-- ------------------------------------------------------------------------- 
+
+let default_parser = parse;;
+
+let secondary_parser = parset;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printing of terms.                                                        
+-- ------------------------------------------------------------------------- 
+{-
+let rec print_term prec fm =
+  match fm with
+    Var x -> print_string x
+  | Fn("^",[tm1;tm2]) -> print_infix_term true prec 24 "^" tm1 tm2
+  | Fn("/",[tm1;tm2]) -> print_infix_term true prec 22 " /" tm1 tm2
+  | Fn("*",[tm1;tm2]) -> print_infix_term false prec 20 " *" tm1 tm2
+  | Fn("-",[tm1;tm2]) -> print_infix_term true prec 18 " -" tm1 tm2
+  | Fn("+",[tm1;tm2]) -> print_infix_term false prec 16 " +" tm1 tm2
+  | Fn("::",[tm1;tm2]) -> print_infix_term false prec 14 "::" tm1 tm2
+  | Fn(f,args) -> print_fargs f args
+
+and print_fargs f args =
+  print_string f;
+  if args = [] then () else
+   (print_string "(";
+    open_box 0;
+    print_term 0 (hd args); print_break 0 0;
+    do_list (fun t -> print_string ","; print_break 0 0; print_term 0 t)
+            (tl args);
+    close_box();
+    print_string ")")
+
+and print_infix_term isleft oldprec newprec sym p q =
+  if oldprec > newprec then (print_string "("; open_box 0) else ();
+  print_term (if isleft then newprec else newprec+1) p;
+  print_string sym;
+  print_break (if String.sub sym 0 1 = " " then 1 else 0) 0;
+  print_term (if isleft then newprec+1 else newprec) q;
+  if oldprec > newprec then (close_box(); print_string ")") else ();;
+
+let printert tm =
+  open_box 0; print_string "<<|";
+  open_box 0; print_term 0 tm; close_box();
+  print_string "|>>"; close_box();;
+
+#install_printer printert;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas.                                                     
+-- ------------------------------------------------------------------------- 
+
+let print_atom prec (R(p,args)) =
+  if mem p ["="; "<"; "<="; ">"; ">="] & length args = 2
+  then print_infix_term false 12 12 (" "^p) (el 0 args) (el 1 args)
+  else print_fargs p args;;
+
+let print_fol_formula = print_qformula print_atom;;
+
+#install_printer print_fol_formula;;
+
+-- ------------------------------------------------------------------------- 
+-- Examples in the main text.                                                
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+<<forall x y. exists z. x < z /\ y < z>>;;
+
+<<~(forall x. P(x)) <=> exists y. ~P(y)>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Free variables in terms and formulas.                                     
+-- ------------------------------------------------------------------------- 
+
+-- | Return all variables occurring in a formula.
+var :: forall formula atom term v.
+       (FirstOrderFormula formula atom v,
+        Atom atom term v) => formula -> Set.Set v
+var fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.insert x (var p)
+      co ((:~:) p) = var p
+      co (BinOp p _ q) = Set.union (var p) (var q)
+      tf _ = Set.empty
+      at :: atom -> Set.Set v
+      at = allVariables
+
+-- | Return the variables that occur free in a formula.
+fv :: forall formula atom term v.
+      (FirstOrderFormula formula atom v,
+       Atom atom term v) => formula -> Set.Set v
+fv fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.delete x (fv p)
+      co ((:~:) p) = fv p
+      co (BinOp p _ q) = Set.union (fv p) (fv q)
+      tf _ = Set.empty
+      at = allVariables
+
+-- | Return the variables in a propositional formula.
+fv' :: forall formula atom term v. (PropositionalFormula formula atom, Atom atom term v, Ord v) => formula -> Set.Set v
+fv' fm =
+    foldPropositional co tf allVariables fm
+    where
+      co ((:~:) p) = fv' p
+      co (BinOp p _ q) = Set.union (fv' p) (fv' q)
+      tf _ = Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Universal closure of a formula.                                           
+-- ------------------------------------------------------------------------- 
+
+generalize :: (FirstOrderFormula formula atom v, Atom atom term v) => formula -> formula
+generalize fm = Set.fold for_all fm (fv fm)
+
+-- ------------------------------------------------------------------------- 
+-- Substitution in formulas, with variable renaming.                         
+-- ------------------------------------------------------------------------- 
+
+subst :: (FirstOrderFormula formula atom v,
+          Term term v f,
+          Atom atom term v) =>
+         Map.Map v term -> formula -> formula
+subst env fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x p = quant op x' (subst ((x |-> vt x') env) p)
+          where
+            x' = if setAny (\ y -> Set.member x (fvt (fromMaybe (vt y) (Map.lookup y env)))) (Set.delete x (fv p))
+                 then variant x (fv (subst (Map.delete x env) p))
+                 else x
+      co ((:~:) p) = ((.~.) (subst env p))
+      co (BinOp p op q) = binop (subst env p) op (subst env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+subst' :: (PropositionalFormula formula atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          Map.Map v term -> formula -> formula
+subst' env fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = ((.~.) (subst' env p))
+      co (BinOp p op q) = binop (subst' env p) op (subst' env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+{-
+-- |Replace each free occurrence of variable old with term new.
+substitute :: forall formula atom term v f. (FirstOrderFormula formula atom v, Term term v f) => v -> term -> (atom -> formula) -> formula -> formula
+substitute old new atom formula =
+    foldTerm (\ new' -> if old == new' then formula else substitute' formula)
+             (\ _ _ -> substitute' formula)
+             new
+    where
+      substitute' =
+          foldFirstOrder -- If the old variable appears in a quantifier
+                -- we can stop doing the substitution.
+                (\ q v f' -> quant q v (if old == v then f' else substitute' f'))
+                (\ cm -> case cm of
+                           ((:~:) f') -> combine ((:~:) (substitute' f'))
+                           (BinOp f1 op f2) -> combine (BinOp (substitute' f1) op (substitute' f2)))
+                fromBool
+                atom
+-}
+{-
+    substitute old new atom formula
+    where 
+      atom = foldAtomEq (\ p ts -> pApp p (map st ts)) fromBool (\ t1 t2 -> st t1 .=. st t2)
+      st :: term -> term
+      st t = foldTerm sv (\ func ts -> fApp func (map st ts)) t
+      sv v = if v == old then new else vt v
+-}
+
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,211 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.FirstOrder
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Negate ((.~.))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: FirstOrderFormula formula atom v => formula -> formula
+antecedent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = err
+      err = error "antecedent"
+
+consequent :: FirstOrderFormula formula atom v => formula -> formula
+consequent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = err
+      err = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+on_atoms f fm =
+    foldFirstOrder qu co tf at fm
+    where 
+      qu op v fm' = quant op v (on_atoms f fm')
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at = f
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: FirstOrderFormula formula atom v => (atom -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldFirstOrder qu co tf pr fm
+    where
+      qu _ _ p = over_atoms f p b
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      pr atom = f atom b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (FirstOrderFormula formula atom v, Ord b) => (atom -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,208 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.Propositional
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+--import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: PropositionalFormula formula atomic => formula -> formula
+antecedent formula =
+    foldPropositional c (error "antecedent") (error "antecedent") formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = error "antecedent"
+
+consequent :: PropositionalFormula formula atomic => formula -> formula
+consequent formula =
+    foldPropositional c (error "consequent") (error "consequent") formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: PropositionalFormula formula atomic => (atomic -> formula) -> formula -> formula
+on_atoms f fm =
+    foldPropositional co tf at fm
+    where 
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at x = f x
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: (PropositionalFormula formula atomic) => (atomic -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      at x = f x b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (PropositionalFormula formula atomic, Ord b) => (atomic -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Herbrand.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Herbrand.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Herbrand.hs
@@ -0,0 +1,309 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Harrison.Herbrand where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom(substitute, freeVariables))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (fApp, Term)
+import Data.Logic.Harrison.DP (dpll)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib')
+import Data.Logic.Harrison.Normal (trivial)
+import Data.Logic.Harrison.Prop (eval, simpcnf, simpdnf)
+import Data.Logic.Harrison.Skolem (functions, runSkolem, skolemize)
+import qualified Data.Map as Map (fromList, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, insert, map, minView, null, partition, Set, singleton, size, toList, union)
+import Data.String (IsString(..))
+
+-- ========================================================================= 
+-- Relation between FOL and propositonal logic; Herbrand theorem.            
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Propositional valuation.                                                  
+-- ------------------------------------------------------------------------- 
+
+pholds :: (PropositionalFormula formula atom, Ord atom) => (Map.Map atom Bool) -> formula -> Bool
+pholds d fm = eval fm d
+
+-- ------------------------------------------------------------------------- 
+-- Get the constants for Herbrand base, adding nullary one if necessary.     
+-- ------------------------------------------------------------------------- 
+
+herbfuns :: forall pf atom term v f. (PropositionalFormula pf atom, Formula pf atom, Atom atom term v, Term term v f, IsString f, Ord f) =>
+            (atom -> Set.Set (f, Int))
+         -> pf
+         -> (Set.Set (f, Int), Set.Set (f, Int))
+herbfuns fa fm =
+  let (cns,fns) = Set.partition (\ (_,ar) -> ar == 0) (functions fa fm) in
+  if Set.null cns then (Set.singleton (fromString "c",0),fns) else (cns,fns)
+
+-- ------------------------------------------------------------------------- 
+-- Enumeration of ground terms and m-tuples, ordered by total fns.           
+-- ------------------------------------------------------------------------- 
+
+groundterms :: forall term v f. (Term term v f) =>
+               Set.Set term -> Set.Set (f, Int) -> Int -> Set.Set term
+groundterms cntms _ 0 = cntms
+groundterms cntms funcs n =
+    Set.fold terms Set.empty funcs
+    where
+      terms (f,m) l = Set.union (Set.map (fApp f) (groundtuples cntms funcs (n - 1) m)) l
+
+groundtuples :: forall term v f. (Term term v f) =>
+                Set.Set term -> Set.Set (f, Int) -> Int -> Int -> Set.Set [term]
+groundtuples _ _ 0 0 = Set.singleton []
+groundtuples _ _ _ 0 = Set.empty
+groundtuples cntms funcs n m =
+    Set.fold tuples Set.empty (Set.fromList [0 .. n])
+    where 
+      tuples k l = Set.union (allpairs (:) (groundterms cntms funcs k) (groundtuples cntms funcs (n - k) (m - 1))) l
+
+-- ------------------------------------------------------------------------- 
+-- Iterate modifier "mfn" over ground terms till "tfn" fails.                
+-- ------------------------------------------------------------------------- 
+
+herbloop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v) =>
+            (Set.Set (Set.Set lit) -> (lit -> lit) -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit))
+         -> (Set.Set (Set.Set lit) -> Failing Bool)
+         -> Set.Set (Set.Set lit)
+         -> Set.Set term
+         -> Set.Set (f, Int)
+         -> [v]
+         -> Int
+         -> Set.Set (Set.Set lit)
+         -> Set.Set [term]
+         -> Set.Set [term]
+         -> Failing (Set.Set [term])
+herbloop mfn tfn fl0 cntms funcs fvs n fl tried tuples =
+{-
+  print_string(string_of_int(length tried) ++ " ground instances tried; " ++
+               string_of_int(length fl) ++ " items in list")
+  print_newline();
+-}
+  case Set.minView tuples of
+    Nothing ->
+          let newtups = groundtuples cntms funcs n (length fvs) in
+          herbloop mfn tfn fl0 cntms funcs fvs (n + 1) fl tried newtups
+    Just (tup, tups) ->
+        let fpf' = Map.fromList (zip fvs tup) in
+        let fl' = mfn fl0 (subst' fpf') fl in
+        case tfn fl' of
+          Failure msgs -> Failure msgs
+          Success x ->
+              if not x
+              then Success (Set.insert tup tried)
+              else herbloop mfn tfn fl0 cntms funcs fvs n fl' (Set.insert tup tried) tups
+
+subst' :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => Map.Map v term -> lit -> lit
+subst' env fm =
+    mapAtoms (atomic . substitute') fm
+    where substitute' :: atom -> atom
+          substitute' = substitute env
+
+-- ------------------------------------------------------------------------- 
+-- Hence a simple Gilmore-type procedure.                                    
+-- ------------------------------------------------------------------------- 
+
+gilmore_loop :: (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit)
+             -> Set.Set term
+             -> Set.Set (f, Int)
+             -> [v]
+             -> Int
+             -> Set.Set (Set.Set lit)
+             -> Set.Set [term]
+             -> Set.Set [term]
+             -> Failing (Set.Set [term])
+gilmore_loop =
+    herbloop mfn (Success . not . Set.null)
+    where
+      mfn djs0 ifn djs = Set.filter (not . trivial) (distrib' (Set.map (Set.map ifn) djs0) djs)
+
+gilmore :: forall fof pf atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            Atom atom term v,
+            IsString f,
+            Ord pf) =>
+           (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+gilmore fa fm =
+  let sfm = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union s (freeVariables a)) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+  gilmore_loop (simpdnf sfm :: Set.Set (Set.Set pf)) cntms funcs (fvs) 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- First example and a little tracing.                                       
+-- ------------------------------------------------------------------------- 
+{-
+test01 =
+    let fm = exists "x" (for_all "y" (pApp "p" [vt "x"] .=>. pApp "p" [vt "y"]))
+        sfm = skolemize ((.~.) fm) in
+    TestList [TestCase (assertEqual "gilmore 1" 2 (gilmore fm))]
+
+START_INTERACTIVE;;
+gilmore <<exists x. forall y. P(x) ==> P(y)>>;;
+
+let sfm = skolemize(Not <<exists x. forall y. P(x) ==> P(y)>>);;
+
+-- ------------------------------------------------------------------------- 
+-- Quick example.                                                            
+-- ------------------------------------------------------------------------- 
+
+let p24 = gilmore
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x))
+   ==> (exists x. P(x) /\ R(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Slightly less easy example.                                               
+-- ------------------------------------------------------------------------- 
+
+let p45 = gilmore
+ <<(forall x. P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))
+              ==> (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==> L(y)) /\
+                      (forall y. G(y) /\ H(x,y) ==> J(x,y)))
+   ==> (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Apparently intractable example.                                           
+-- ------------------------------------------------------------------------- 
+
+{-
+
+let p20 = gilmore
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+-}
+
+
+-- ------------------------------------------------------------------------- 
+-- The Davis-Putnam procedure for first order logic.                         
+-- ------------------------------------------------------------------------- 
+
+dp_mfn :: (Ord b, Ord a) =>
+          Set.Set (Set.Set a)
+       -> (a -> b)
+       -> Set.Set (Set.Set b)
+       -> Set.Set (Set.Set b)
+dp_mfn cjs0 ifn cjs = Set.union (Set.map (Set.map ifn) cjs0) cjs
+
+dp_loop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+           Set.Set (Set.Set lit)
+        -> Set.Set term
+        -> Set.Set (f, Int)
+        -> [v]
+        -> Int
+        -> Set.Set (Set.Set lit)
+        -> Set.Set [term]
+        -> Set.Set [term]
+        -> Failing (Set.Set [term])
+dp_loop = herbloop dp_mfn dpll
+
+davisputnam :: forall fof atom term v lit f.
+               (FirstOrderFormula fof atom v,
+                PropositionalFormula lit atom,
+                Literal lit atom,
+                Term term v f,
+                Atom atom term v,
+                IsString f,
+                Ord lit) =>
+               (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam fa fm =
+  let (sfm :: lit) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c [] :: term) consts in
+  dp_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- Show how much better than the Gilmore procedure this can be.              
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+let p20 = davisputnam
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Try to cut out useless instantiations in final result.                    
+-- ------------------------------------------------------------------------- 
+
+dp_refine :: (Literal lit atom, Atom atom term v, Term term v f) =>
+             Set.Set (Set.Set lit) -> [v] -> Set.Set [term] -> Set.Set [term] -> Failing (Set.Set [term])
+dp_refine cjs0 fvs dknow need =
+    case Set.minView dknow of
+      Nothing -> Success need
+      Just (cl, dknow') ->
+          let mfn = dp_mfn cjs0 . subst' . Map.fromList . zip fvs in
+          dpll (Set.fold mfn Set.empty (Set.union need dknow')) >>= \ flag ->
+          if flag then return (Set.insert cl need) else return need >>=
+          dp_refine cjs0 fvs dknow'
+
+dp_refine_loop :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+                  Set.Set (Set.Set lit)
+               -> Set.Set term
+               -> Set.Set (f, Int)
+               -> [v]
+               -> Int
+               -> Set.Set (Set.Set lit)
+               -> Set.Set [term]
+               -> Set.Set [term]
+               -> Failing (Set.Set [term])
+dp_refine_loop cjs0 cntms funcs fvs n cjs tried tuples =
+    dp_loop cjs0 cntms funcs fvs n cjs tried tuples >>= \ tups ->
+    dp_refine cjs0 fvs tups Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Show how few of the instances we really need. Hence unification!          
+-- ------------------------------------------------------------------------- 
+
+davisputnam' :: forall fof atom term lit v f pf.
+                (FirstOrderFormula fof atom v,
+                 Literal lit atom,
+                 PropositionalFormula pf atom, -- Formula pf atom,
+                 Term term v f,
+                 Atom atom term v,
+                 IsString f) =>
+                (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam' fa fm =
+    let (sfm :: pf) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+    let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+        (consts,funcs) = herbfuns fa sfm in
+    let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+    dp_refine_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+{-
+START_INTERACTIVE;;
+let p36 = davisputnam'
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+                ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+   ==> (forall x. exists y. H(x,y))>>;;
+
+let p29 = davisputnam'
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/LICENSE.txt b/testdata/split-merge-expected/Data/Logic/Harrison/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/LICENSE.txt
@@ -0,0 +1,35 @@
+IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+By downloading, copying, installing or using the software you agree
+to this license.  If you do not agree to this license, do not
+download, install, copy or use the software.
+
+Copyright (c) 2003-2007, John Harrison
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* The name of John Harrison may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Lib.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Lib.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Lib.hs
@@ -0,0 +1,846 @@
+{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}
+{-# OPTIONS_GHC -Wall -fno-warn-unused-binds #-}
+module Data.Logic.Harrison.Lib
+    ( tests
+    , setAny
+    , setAll
+    -- , itlist2
+    -- , itlist  -- same as foldr with last arguments flipped
+    , tryfind
+    , settryfind
+    -- , end_itlist -- same as foldr1
+    , (|=>)
+    , (|->)
+    , fpf
+    , defined
+    , apply
+    , exists
+    , tryApplyD
+    , allpairs
+    , distrib'
+    , image
+    , optimize
+    , minimize
+    , maximize
+    , optimize'
+    , minimize'
+    , maximize'
+    , can
+    , allsets
+    , allsubsets
+    , allnonemptysubsets
+    , mapfilter
+    , setmapfilter
+    , (∅)
+    ) where
+
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+import Data.Maybe
+import qualified Data.Set as Set
+import Test.HUnit (Test(TestCase, TestList, TestLabel), assertEqual)
+
+(∅) :: Set.Set a
+(∅) = Set.empty
+
+tests :: Test
+tests = TestLabel "Data.Logic.Harrison.Lib" $ TestList [test01]
+
+setAny :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAny f s = Set.member True (Set.map f s)
+
+setAll :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAll f s = not (Set.member False (Set.map f s))
+
+{-
+(* ========================================================================= *)
+(* Misc library functions to set up a nice environment.                      *)
+(* ========================================================================= *)
+
+let identity x = x;;
+
+let ( ** ) = fun f g x -> f(g x);;
+
+(* ------------------------------------------------------------------------- *)
+(* GCD and LCM on arbitrary-precision numbers.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let gcd_num n1 n2 =
+  abs_num(num_of_big_int
+      (Big_int.gcd_big_int (big_int_of_num n1) (big_int_of_num n2)));;
+
+let lcm_num n1 n2 = abs_num(n1 */ n2) // gcd_num n1 n2;;
+
+(* ------------------------------------------------------------------------- *)
+(* A useful idiom for "non contradictory" etc.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let non p x = not(p x);;
+
+(* ------------------------------------------------------------------------- *)
+(* Kind of assertion checking.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let check p x = if p(x) then x else failwith "check";;
+
+(* ------------------------------------------------------------------------- *)
+(* Repetition of a function.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec funpow n f x =
+  if n < 1 then x else funpow (n-1) f (f x);;
+-}
+-- let can f x = try f x; true with Failure _ -> false;;
+can :: (t -> Failing a) -> t -> Bool
+can f x = failing (const True) (const False) (f x)
+
+{-
+let rec repeat f x = try repeat f (f x) with Failure _ -> x;;
+
+(* ------------------------------------------------------------------------- *)
+(* Handy list operations.                                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec (--) = fun m n -> if m > n then [] else m::((m + 1) -- n);;
+
+let rec (---) = fun m n -> if m >/ n then [] else m::((m +/ Int 1) --- n);;
+
+let rec map2 f l1 l2 =
+  match (l1,l2) with
+    [],[] -> []
+  | (h1::t1),(h2::t2) -> let h = f h1 h2 in h::(map2 f t1 t2)
+  | _ -> failwith "map2: length mismatch";;
+
+let rev =
+  let rec rev_append acc l =
+    match l with
+      [] -> acc
+    | h::t -> rev_append (h::acc) t in
+  fun l -> rev_append [] l;;
+
+let hd l =
+  match l with
+   h::t -> h
+  | _ -> failwith "hd";;
+
+let tl l =
+  match l with
+   h::t -> t
+  | _ -> failwith "tl";;
+-}
+
+-- (^) = (++)
+
+itlist :: (a -> b -> b) -> [a] -> b -> b
+-- itlist f xs z = foldr f z xs
+itlist f xs z = foldr f z xs
+
+end_itlist :: (t -> t -> t) -> [t] -> t
+-- end_itlist = foldr1
+end_itlist = foldr1
+
+itlist2 :: (t -> t1 -> Failing t2 -> Failing t2) -> [t] -> [t1] -> Failing t2 -> Failing t2
+itlist2 f l1 l2 b =
+  case (l1,l2) of
+    ([],[]) -> b
+    (h1 : t1, h2 : t2) -> f h1 h2 (itlist2 f t1 t2 b)
+    _ -> Failure ["itlist2"]
+
+{-
+let rec zip l1 l2 =
+  match (l1,l2) with
+        ([],[]) -> []
+      | (h1::t1,h2::t2) -> (h1,h2)::(zip t1 t2)
+      | _ -> failwith "zip";;
+
+let rec forall p l =
+  match l with
+    [] -> true
+  | h::t -> p(h) & forall p t;;
+-}
+exists :: (a -> Bool) -> [a] -> Bool
+exists = any
+{-
+let partition p l =
+    itlist (fun a (yes,no) -> if p a then a::yes,no else yes,a::no) l ([],[]);;
+
+let filter p l = fst(partition p l);;
+
+let length =
+  let rec len k l =
+    if l = [] then k else len (k + 1) (tl l) in
+  fun l -> len 0 l;;
+
+let rec last l =
+  match l with
+    [x] -> x
+  | (h::t) -> last t
+  | [] -> failwith "last";;
+
+let rec butlast l =
+  match l with
+    [_] -> []
+  | (h::t) -> h::(butlast t)
+  | [] -> failwith "butlast";;
+
+let rec find p l =
+  match l with
+      [] -> failwith "find"
+    | (h::t) -> if p(h) then h else find p t;;
+
+let rec el n l =
+  if n = 0 then hd l else el (n - 1) (tl l);;
+
+let map f =
+  let rec mapf l =
+    match l with
+      [] -> []
+    | (x::t) -> let y = f x in y::(mapf t) in
+  mapf;;
+-}
+
+allpairs :: forall a b c. (Ord c) => (a -> b -> c) -> Set.Set a -> Set.Set b -> Set.Set c
+-- allpairs f xs ys = Set.fromList (concatMap (\ z -> map (f z) (Set.toList ys)) (Set.toList xs))
+allpairs f xs ys = Set.fold (\ x zs -> Set.fold (\ y zs' -> Set.insert (f x y) zs') zs ys) Set.empty xs
+
+distrib' :: Ord a => Set.Set (Set.Set a) -> Set.Set (Set.Set a) -> Set.Set (Set.Set a)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+test01 :: Test
+test01 = TestCase $ assertEqual "itlist2" expected input
+    where input = allpairs (,) (Set.fromList [1,2,3]) (Set.fromList [4,5,6])
+          expected = Set.fromList [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)] :: Set.Set (Int, Int)
+
+{-
+let rec distinctpairs l =
+  match l with
+   x::t -> itlist (fun y a -> (x,y) :: a) t (distinctpairs t)
+  | [] -> [];;
+
+let rec chop_list n l =
+  if n = 0 then [],l else
+  try let m,l' = chop_list (n-1) (tl l) in (hd l)::m,l'
+  with Failure _ -> failwith "chop_list";;
+
+let replicate n a = map (fun x -> a) (1--n);;
+
+let rec insertat i x l =
+  if i = 0 then x::l else
+  match l with
+    [] -> failwith "insertat: list too short for position to exist"
+  | h::t -> h::(insertat (i-1) x t);;
+
+let rec forall2 p l1 l2 =
+  match (l1,l2) with
+    [],[] -> true
+  | (h1::t1,h2::t2) -> p h1 h2 & forall2 p t1 t2
+  | _ -> false;;
+
+let index x =
+  let rec ind n l =
+    match l with
+      [] -> failwith "index"
+    | (h::t) -> if Pervasives.compare x h = 0 then n else ind (n + 1) t in
+  ind 0;;
+
+let rec unzip l =
+  match l with
+    [] -> [],[]
+  | (x,y)::t ->
+      let xs,ys = unzip t in x::xs,y::ys;;
+
+(* ------------------------------------------------------------------------- *)
+(* Whether the first of two items comes earlier in the list.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec earlier l x y =
+  match l with
+    h::t -> (Pervasives.compare h y <> 0) &
+            (Pervasives.compare h x = 0 or earlier t x y)
+  | [] -> false;;
+
+(* ------------------------------------------------------------------------- *)
+(* Application of (presumably imperative) function over a list.              *)
+(* ------------------------------------------------------------------------- *)
+
+let rec do_list f l =
+  match l with
+    [] -> ()
+  | h::t -> f(h); do_list f t;;
+
+(* ------------------------------------------------------------------------- *)
+(* Association lists.                                                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare x a = 0 then y else assoc a t
+  | [] -> failwith "find";;
+
+let rec rev_assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare y a = 0 then x else rev_assoc a t
+  | [] -> failwith "find";;
+
+(* ------------------------------------------------------------------------- *)
+(* Merging of sorted lists (maintaining repetitions).                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec merge ord l1 l2 =
+  match l1 with
+    [] -> l2
+  | h1::t1 -> match l2 with
+                [] -> l1
+              | h2::t2 -> if ord h1 h2 then h1::(merge ord t1 l2)
+                          else h2::(merge ord l1 t2);;
+
+(* ------------------------------------------------------------------------- *)
+(* Bottom-up mergesort.                                                      *)
+(* ------------------------------------------------------------------------- *)
+
+let sort ord =
+  let rec mergepairs l1 l2 =
+    match (l1,l2) with
+        ([s],[]) -> s
+      | (l,[]) -> mergepairs [] l
+      | (l,[s1]) -> mergepairs (s1::l) []
+      | (l,(s1::s2::ss)) -> mergepairs ((merge ord s1 s2)::l) ss in
+  fun l -> if l = [] then [] else mergepairs [] (map (fun x -> [x]) l);;
+
+(* ------------------------------------------------------------------------- *)
+(* Common measure predicates to use with "sort".                             *)
+(* ------------------------------------------------------------------------- *)
+
+let increasing f x y = Pervasives.compare (f x) (f y) < 0;;
+
+let decreasing f x y = Pervasives.compare (f x) (f y) > 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* Eliminate repetitions of adjacent elements, with and without counting.    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec uniq l =
+  match l with
+    x::(y::_ as t) -> let t' = uniq t in
+                      if Pervasives.compare x y = 0 then t' else
+                      if t'==t then l else x::t'
+ | _ -> l;;
+
+let repetitions =
+  let rec repcount n l =
+    match l with
+      x::(y::_ as ys) -> if Pervasives.compare y x = 0 then repcount (n + 1) ys
+                  else (x,n)::(repcount 1 ys)
+    | [x] -> [x,n]
+    | [] -> failwith "repcount" in
+  fun l -> if l = [] then [] else repcount 1 l;;
+-}
+
+tryfind :: (t -> Failing a) -> [t] -> Failing a
+tryfind _ [] = Failure ["tryfind"]
+tryfind f l =
+    case l of
+      [] -> Failure ["tryfind"]
+      h : t -> failing (\ _ -> tryfind f t) Success (f h)
+
+settryfind :: (t -> Failing a) -> Set.Set t -> Failing a
+settryfind f l =
+    case Set.minView l of
+      Nothing -> Failure ["settryfind"]
+      Just (h, t) -> failing (\ _ -> settryfind f t) Success (f h)
+
+mapfilter :: (a -> Failing b) -> [a] -> [b]
+mapfilter f l = catMaybes (map (failing (const Nothing) Just . f) l) 
+    -- filter (failing (const False) (const True)) (map f l)
+
+setmapfilter :: Ord b => (a -> Failing b) -> Set.Set a -> Set.Set b
+setmapfilter f s = Set.fold (\ a r -> failing (const r) (`Set.insert` r) (f a)) Set.empty s
+
+-- -------------------------------------------------------------------------
+-- Find list member that maximizes or minimizes a function.                 
+-- -------------------------------------------------------------------------
+
+optimize :: forall a b. (b -> b -> Bool) -> (a -> b) -> [a] -> Maybe a
+optimize _ _ [] = Nothing
+optimize ord f l = Just (fst (foldr1 (\ p@(_,y) p'@(_,y') -> if ord y y' then p else p') (map (\ x -> (x,f x)) l)))
+
+maximize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+maximize f l = optimize (>) f l
+
+minimize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+minimize f l = optimize (<) f l
+
+optimize' :: forall a b. (b -> b -> Bool) -> (a -> b) -> Set.Set a -> Maybe a
+optimize' ord f s = optimize ord f (Set.toList s)
+
+maximize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+maximize' f s = optimize' (>) f s
+
+minimize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+minimize' f s = optimize' (<) f s
+
+-- -------------------------------------------------------------------------
+-- Set operations on ordered lists.                                         
+-- -------------------------------------------------------------------------
+{-
+let setify =
+  let rec canonical lis =
+     match lis with
+       x::(y::_ as rest) -> Pervasives.compare x y < 0 & canonical rest
+     | _ -> true in
+  fun l -> if canonical l then l
+           else uniq (sort (fun x y -> Pervasives.compare x y <= 0) l);;
+
+let union =
+  let rec union l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> l2
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(union t1 t2)
+          else if h1 < h2 then h1::(union t1 l2)
+          else h2::(union l1 t2) in
+  fun s1 s2 -> union (setify s1) (setify s2);;
+
+let intersect =
+  let rec intersect l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> []
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(intersect t1 t2)
+          else if h1 < h2 then intersect t1 l2
+          else intersect l1 t2 in
+  fun s1 s2 -> intersect (setify s1) (setify s2);;
+
+let subtract =
+  let rec subtract l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then subtract t1 t2
+          else if h1 < h2 then h1::(subtract t1 l2)
+          else subtract l1 t2 in
+  fun s1 s2 -> subtract (setify s1) (setify s2);;
+
+let subset,psubset =
+  let rec subset l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> true
+      | (l1,[]) -> false
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then subset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2
+  and psubset l1 l2 =
+    match (l1,l2) with
+        (l1,[]) -> false
+      | ([],l2) -> true
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then psubset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2 in
+  (fun s1 s2 -> subset (setify s1) (setify s2)),
+  (fun s1 s2 -> psubset (setify s1) (setify s2));;
+
+let rec set_eq s1 s2 = (setify s1 = setify s2);;
+
+let insert x s = union [x] s;;
+-}
+
+image :: (Ord b, Ord a) => (a -> b) -> Set.Set a -> Set.Set b
+image f s = Set.map f s
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Union of a family of sets.                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let unions s = setify(itlist (@) s []);;
+
+(* ------------------------------------------------------------------------- *)
+(* List membership. This does *not* assume the list is a set.                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec mem x lis =
+  match lis with
+    [] -> false
+  | (h::t) -> Pervasives.compare x h = 0 or mem x t;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Finding all subsets or all subsets of a given size.                       
+-- ------------------------------------------------------------------------- 
+
+-- allsets :: Ord a => Int -> Set.Set a -> Set.Set (Set.Set a)
+allsets :: forall a b. (Num a, Eq a, Ord b) => a -> Set.Set b -> Set.Set (Set.Set b)
+allsets 0 _ = Set.singleton Set.empty
+allsets m l =
+    case Set.minView l of
+      Nothing -> Set.empty
+      Just (h, t) -> Set.union (Set.map (Set.insert h) (allsets (m - 1) t)) (allsets m t)
+
+allsubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allsubsets s =
+    maybe (Set.singleton Set.empty)
+          (\ (x, t) -> 
+               let res = allsubsets t in
+               Set.union res (Set.map (Set.insert x) res))
+          (Set.minView s)
+
+
+allnonemptysubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allnonemptysubsets s = Set.delete Set.empty (allsubsets s)
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Explosion and implosion of strings.                                       *)
+(* ------------------------------------------------------------------------- *)
+
+let explode s =
+  let rec exap n l =
+     if n < 0 then l else
+      exap (n - 1) ((String.sub s n 1)::l) in
+  exap (String.length s - 1) [];;
+
+let implode l = itlist (^) l "";;
+
+(* ------------------------------------------------------------------------- *)
+(* Timing; useful for documentation but not logically necessary.             *)
+(* ------------------------------------------------------------------------- *)
+
+let time f x =
+  let start_time = Sys.time() in
+  let result = f x in
+  let finish_time = Sys.time() in
+  print_string
+    ("CPU time (user): "^(string_of_float(finish_time -. start_time)));
+  print_newline();
+  result;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Polymorphic finite partial functions via Patricia trees.                 
+--                                                                          
+-- The point of this strange representation is that it is canonical (equal  
+-- functions have the same encoding) yet reasonably efficient on average.   
+--                                                                          
+-- Idea due to Diego Olivier Fernandez Pons (OCaml list, 2003/11/10).       
+-- -------------------------------------------------------------------------
+{-
+data Func a b
+    = Empty
+    | Leaf Int [(a, b)]
+    | Branch Int Int (Func a b) (Func a b)
+
+-- -------------------------------------------------------------------------
+-- Undefined function.                                                      
+-- -------------------------------------------------------------------------
+
+undefinedFunction = Empty
+
+-- -------------------------------------------------------------------------
+-- In case of equality comparison worries, better use this.                 
+-- -------------------------------------------------------------------------
+
+isUndefined Empty = True
+isUndefined _ = False
+
+-- -------------------------------------------------------------------------
+-- Operation analogous to "map" for functions.                                  
+-- -------------------------------------------------------------------------
+
+mapf f t =
+    case t of
+      Empty -> Empty
+      Leaf h l -> Leaf h (map_list f l)
+      Branch p b l r -> Branch p b (mapf f l) (mapf f r)
+    where
+      map_list f l =
+          case l of
+            [] -> []
+            (x,y) : t -> (x, f y) : map_list f t
+
+-- -------------------------------------------------------------------------
+-- Operations analogous to "fold" for lists.                                
+-- -------------------------------------------------------------------------
+
+foldlFn f a t =
+    case t of
+      Empty -> a
+      Leaf h l -> foldl_list f a l
+      Branch p b l r -> foldlFn f (foldlFn f a l) r
+    where
+      foldl_list f a l =
+          case l of
+            [] -> a
+            (x,y) : t -> foldl_list f (f a x y) t
+
+foldrFn f t a =
+    case t of
+      Empty -> a
+      Leaf h l -> foldr_list f l a
+      Branch p b l r -> foldrFn f l (foldrFn f r a)
+    where
+      foldr_list f l a =
+          case l of
+            [] -> a
+            (x, y) : t -> f x y (foldr_list f t a)
+
+-- -------------------------------------------------------------------------
+-- Mapping to sorted-list representation of the graph, domain and range.    
+-- -------------------------------------------------------------------------
+
+graph f = Set.fromList (foldlFn (\ a x y -> (x,y) : a) [] f)
+
+dom f = Set.fromList (foldlFn (\ a x y -> x :a) [] f)
+
+ran f = Set.fromList (foldlFn (\ a x y -> y : a) [] f)
+-}
+
+-- -------------------------------------------------------------------------
+-- Application.                                                             
+-- -------------------------------------------------------------------------
+
+applyD :: Ord k => Map.Map k a -> k -> a -> Map.Map k a
+applyD m k a = Map.insert k a m
+
+apply :: Ord k => Map.Map k a -> k -> Maybe a
+apply m k = Map.lookup k m
+
+tryApplyD :: Ord k => Map.Map k a -> k -> a -> a
+tryApplyD m k d = fromMaybe d (Map.lookup k m)
+
+tryApplyL :: Ord k => Map.Map k [a] -> k -> [a]
+tryApplyL m k = tryApplyD m k []
+{-
+applyD :: (t -> Maybe b) -> (t -> b) -> t -> b
+applyD f d x = maybe (d x) id (f x)
+
+apply :: (t -> Maybe b) -> t -> b
+apply f = applyD f (\ _ -> error "apply")
+
+tryApplyD :: (t -> Maybe b) -> t -> b -> b
+tryApplyD f a d = maybe d id (f a)
+
+tryApplyL :: (t -> Maybe [a]) -> t -> [a]
+tryApplyL f x = tryApplyD f x []
+-}
+
+defined :: Ord t => Map.Map t a -> t -> Bool
+defined = flip Map.member
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Undefinition.                                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let undefine =
+  let rec undefine_list x l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then t
+          else if c < 0 then l else
+          let t' = undefine_list x t in
+          if t' == t then l else ab::t'
+    | [] -> [] in
+  fun x ->
+    let k = Hashtbl.hash x in
+    let rec und t =
+      match t with
+        Leaf(h,l) when h = k ->
+          let l' = undefine_list x l in
+          if l' == l then t
+          else if l' = [] then Empty
+          else Leaf(h,l')
+      | Branch(p,b,l,r) when k land (b - 1) = p ->
+          if k land b = 0 then
+            let l' = und l in
+            if l' == l then t
+            else (match l' with Empty -> r | _ -> Branch(p,b,l',r))
+          else
+            let r' = und r in
+            if r' == r then t
+            else (match r' with Empty -> l | _ -> Branch(p,b,l,r'))
+      | _ -> t in
+    und;;
+
+(* ------------------------------------------------------------------------- *)
+(* Redefinition and combination.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let (|->),combine =
+  let newbranch p1 t1 p2 t2 =
+    let zp = p1 lxor p2 in
+    let b = zp land (-zp) in
+    let p = p1 land (b - 1) in
+    if p1 land b = 0 then Branch(p,b,t1,t2)
+    else Branch(p,b,t2,t1) in
+  let rec define_list (x,y as xy) l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then xy::t
+          else if c < 0 then xy::l
+          else ab::(define_list xy t)
+    | [] -> [xy]
+  and combine_list op z l1 l2 =
+    match (l1,l2) with
+      [],_ -> l2
+    | _,[] -> l1
+    | ((x1,y1 as xy1)::t1,(x2,y2 as xy2)::t2) ->
+          let c = Pervasives.compare x1 x2 in
+          if c < 0 then xy1::(combine_list op z t1 l2)
+          else if c > 0 then xy2::(combine_list op z l1 t2) else
+          let y = op y1 y2 and l = combine_list op z t1 t2 in
+          if z(y) then l else (x1,y)::l in
+  let (|->) x y =
+    let k = Hashtbl.hash x in
+    let rec upd t =
+      match t with
+        Empty -> Leaf (k,[x,y])
+      | Leaf(h,l) ->
+           if h = k then Leaf(h,define_list (x,y) l)
+           else newbranch h t k (Leaf(k,[x,y]))
+      | Branch(p,b,l,r) ->
+          if k land (b - 1) <> p then newbranch p t k (Leaf(k,[x,y]))
+          else if k land b = 0 then Branch(p,b,upd l,r)
+          else Branch(p,b,l,upd r) in
+    upd in
+  let rec combine op z t1 t2 =
+    match (t1,t2) with
+      Empty,_ -> t2
+    | _,Empty -> t1
+    | Leaf(h1,l1),Leaf(h2,l2) ->
+          if h1 = h2 then
+            let l = combine_list op z l1 l2 in
+            if l = [] then Empty else Leaf(h1,l)
+          else newbranch h1 t1 h2 t2
+    | (Leaf(k,lis) as lf),(Branch(p,b,l,r) as br) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z lf l with
+                 Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z lf r with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch k lf p br
+    | (Branch(p,b,l,r) as br),(Leaf(k,lis) as lf) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z l lf with
+                Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z r lf with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch p br k lf
+    | Branch(p1,b1,l1,r1),Branch(p2,b2,l2,r2) ->
+          if b1 < b2 then
+            if p2 land (b1 - 1) <> p1 then newbranch p1 t1 p2 t2
+            else if p2 land b1 = 0 then
+              (match combine op z l1 t2 with
+                 Empty -> r1 | l -> Branch(p1,b1,l,r1))
+            else
+              (match combine op z r1 t2 with
+                 Empty -> l1 | r -> Branch(p1,b1,l1,r))
+          else if b2 < b1 then
+            if p1 land (b2 - 1) <> p2 then newbranch p1 t1 p2 t2
+            else if p1 land b2 = 0 then
+              (match combine op z t1 l2 with
+                 Empty -> r2 | l -> Branch(p2,b2,l,r2))
+            else
+              (match combine op z t1 r2 with
+                 Empty -> l2 | r -> Branch(p2,b2,l2,r))
+          else if p1 = p2 then
+           (match (combine op z l1 l2,combine op z r1 r2) with
+              (Empty,r) -> r | (l,Empty) -> l | (l,r) -> Branch(p1,b1,l,r))
+          else
+            newbranch p1 t1 p2 t2 in
+  (|->),combine;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Special case of point function.                                          
+-- -------------------------------------------------------------------------
+
+(|=>) :: Ord k => k -> a -> Map.Map k a
+x |=> y = Map.fromList [(x, y)]
+
+-- -------------------------------------------------------------------------
+-- Idiom for a mapping zipping domain and range lists.                      
+-- -------------------------------------------------------------------------
+
+(|->) :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
+(|->) a b m = Map.insert a b m
+
+fpf :: Ord a => Map.Map a b -> a -> Maybe b
+fpf m a = Map.lookup a m
+
+-- -------------------------------------------------------------------------
+-- Grab an arbitrary element.                                               
+-- -------------------------------------------------------------------------
+
+choose :: Map.Map k a -> (k, a)
+choose = Map.findMin
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Install a (trivial) printer for finite partial functions.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let print_fpf (f:('a,'b)func) = print_string "<func>";;
+
+#install_printer print_fpf;;
+
+(* ------------------------------------------------------------------------- *)
+(* Related stuff for standard functions.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let valmod a y f x = if x = a then y else f(x);;
+
+let undef x = failwith "undefined function";;
+
+(* ------------------------------------------------------------------------- *)
+(* Union-find algorithm.                                                     *)
+(* ------------------------------------------------------------------------- *)
+
+type ('a)pnode = Nonterminal of 'a | Terminal of 'a * int;;
+
+type ('a)partition = Partition of ('a,('a)pnode)func;;
+
+let rec terminus (Partition f as ptn) a =
+  match (apply f a) with
+    Nonterminal(b) -> terminus ptn b
+  | Terminal(p,q) -> (p,q);;
+
+let tryterminus ptn a =
+  try terminus ptn a with Failure _ -> (a,1);;
+
+let canonize ptn a = fst(tryterminus ptn a);;
+
+let equivalent eqv a b = canonize eqv a = canonize eqv b;;
+
+let equate (a,b) (Partition f as ptn) =
+  let (a',na) = tryterminus ptn a
+  and (b',nb) = tryterminus ptn b in
+  Partition
+   (if a' = b' then f else
+    if na <= nb then
+       itlist identity [a' |-> Nonterminal b'; b' |-> Terminal(b',na+nb)] f
+    else
+       itlist identity [b' |-> Nonterminal a'; a' |-> Terminal(a',na+nb)] f);;
+
+let unequal = Partition undefined;;
+
+let equated (Partition f) = dom f;;
+
+(* ------------------------------------------------------------------------- *)
+(* First number starting at n for which p succeeds.                          *)
+(* ------------------------------------------------------------------------- *)
+
+let rec first n p = if p(n) then n else first (n +/ Int 1) p;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Meson.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Meson.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Meson.hs
@@ -0,0 +1,547 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Meson where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), negative)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.FOL (generalize, list_conj)
+import Data.Logic.Harrison.Lib (setAll, settryfind)
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf)
+import Data.Logic.Harrison.Prolog (renamerule)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (deepen, unify_literals)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set as Set (delete, empty, fold, insert, map, Set, size, union)
+
+-- =========================================================================
+-- Model elimination procedure (MESON version, based on Stickel's PTTP).     
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Example of naivety of tableau prover.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+tab <<forall a. ~(P(a) /\ (forall y z. Q(y) \/ R(z)) /\ ~P(a))>>;;
+
+tab <<forall a. ~(P(a) /\ ~P(a) /\ (forall y z. Q(y) \/ R(z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The interesting example where tableaux connections make the proof longer. 
+-- Unfortuntely this gets hammered by normalization first...                 
+-- ------------------------------------------------------------------------- 
+
+tab <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+      (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+      (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Generation of contrapositives.                                            
+-- ------------------------------------------------------------------------- 
+
+contrapositives :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => Set.Set fof -> Set.Set (Set.Set fof, fof)
+contrapositives cls =
+    if setAll negative cls then Set.insert (Set.map (.~.) cls,false) base else base
+    where base = Set.map (\ c -> (Set.map (.~.) (Set.delete c cls), c)) cls
+
+-- ------------------------------------------------------------------------- 
+-- The core of MESON: ancestor unification or Prolog-style extension.        
+-- ------------------------------------------------------------------------- 
+
+mexpand :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set (Set.Set fof, fof)
+        -> Set.Set fof
+        -> fof
+        -> ((Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int))
+        -> (Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int)
+mexpand rules ancestors g cont (env,n,k) =
+    if n < 0
+    then Failure ["Too deep"]
+    else case settryfind doAncestor ancestors of
+           Success a -> Success a
+           Failure _ -> settryfind doRule rules
+    where
+      doAncestor a =
+          do mp <- unify_literals env g ((.~.) a)
+             cont (mp, n, k)
+      doRule rule =
+          do mp <- unify_literals env g c
+             mexpand' (mp, n - Set.size asm, k')
+          where
+            mexpand' = Set.fold (mexpand rules (Set.insert g ancestors)) cont asm
+            ((asm, c), k') = renamerule k rule
+
+-- ------------------------------------------------------------------------- 
+-- Full MESON procedure.                                                     
+-- ------------------------------------------------------------------------- 
+
+puremeson :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+             Maybe Int -> fof -> Failing ((Map.Map v term, Int, Int), Int)
+puremeson maxdl fm =
+    deepen f 0 maxdl
+    where
+      f n = mexpand rules Set.empty false return (Map.empty, n, 0)
+      rules = Set.fold (Set.union . contrapositives) Set.empty cls
+      cls = simpcnf (specialize (pnf fm))
+
+meson :: forall m fof atom term f v. (FirstOrderFormula fof atom v, PropositionalFormula fof atom, Literal fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+         Maybe Int -> fof -> SkolemT v term m (Set.Set (Failing ((Map.Map v term, Int, Int), Int)))
+meson maxdl fm =
+    askolemize ((.~.)(generalize fm)) >>=
+    return . Set.map (puremeson maxdl . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+{-
+-- ------------------------------------------------------------------------- 
+-- With repetition checking and divide-and-conquer search.                   
+-- ------------------------------------------------------------------------- 
+
+let rec equal env fm1 fm2 =
+  try unify_literals env (fm1,fm2) == env with Failure _ -> false;;
+
+let expand2 expfn goals1 n1 goals2 n2 n3 cont env k =
+   expfn goals1 (fun (e1,r1,k1) ->
+        expfn goals2 (fun (e2,r2,k2) ->
+                        if n2 + r1 <= n3 + r2 then failwith "pair"
+                        else cont(e2,r2,k2))
+              (e1,n2+r1,k1))
+        (env,n1,k);;
+
+let rec mexpand rules ancestors g cont (env,n,k) =
+  if n < 0 then failwith "Too deep"
+  else if exists (equal env g) ancestors then failwith "repetition" else
+  try tryfind (fun a -> cont (unify_literals env (g,negate a),n,k))
+              ancestors
+  with Failure _ -> tryfind
+    (fun r -> let (asm,c),k' = renamerule k r in
+              mexpands rules (g::ancestors) asm cont
+                       (unify_literals env (g,c),n-length asm,k'))
+    rules
+
+and mexpands rules ancestors gs cont (env,n,k) =
+  if n < 0 then failwith "Too deep" else
+  let m = length gs in
+  if m <= 1 then itlist (mexpand rules ancestors) gs cont (env,n,k) else
+  let n1 = n / 2 in
+  let n2 = n - n1 in
+  let goals1,goals2 = chop_list (m / 2) gs in
+  let expfn = expand2 (mexpands rules ancestors) in
+  try expfn goals1 n1 goals2 n2 (-1) cont env k
+  with Failure _ -> expfn goals2 n1 goals1 n2 n1 cont env k;;
+
+let puremeson fm =
+  let cls = simpcnf(specialize(pnf fm)) in
+  let rules = itlist ((@) ** contrapositives) cls [] in
+  deepen (fun n ->
+     mexpand rules [] False (fun x -> x) (undefined,n,0); n) 0;;
+
+let meson fm =
+  let fm1 = askolemize(Not(generalize fm)) in
+  map (puremeson ** list_conj) (simpdnf fm1);;
+
+-- ------------------------------------------------------------------------- 
+-- The Los problem (depth 20) and the Steamroller (depth 53) --- lengthier.  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+{- ***********
+
+let los = meson
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+let steamroller = meson
+ <<((forall x. P1(x) ==> P0(x)) /\ (exists x. P1(x))) /\
+   ((forall x. P2(x) ==> P0(x)) /\ (exists x. P2(x))) /\
+   ((forall x. P3(x) ==> P0(x)) /\ (exists x. P3(x))) /\
+   ((forall x. P4(x) ==> P0(x)) /\ (exists x. P4(x))) /\
+   ((forall x. P5(x) ==> P0(x)) /\ (exists x. P5(x))) /\
+   ((exists x. Q1(x)) /\ (forall x. Q1(x) ==> Q0(x))) /\
+   (forall x. P0(x)
+              ==> (forall y. Q0(y) ==> R(x,y)) \/
+                  ((forall y. P0(y) /\ S0(y,x) /\
+                              (exists z. Q0(z) /\ R(y,z))
+                              ==> R(x,y)))) /\
+   (forall x y. P3(y) /\ (P5(x) \/ P4(x)) ==> S0(x,y)) /\
+   (forall x y. P3(x) /\ P2(y) ==> S0(x,y)) /\
+   (forall x y. P2(x) /\ P1(y) ==> S0(x,y)) /\
+   (forall x y. P1(x) /\ (P2(y) \/ Q1(y)) ==> ~(R(x,y))) /\
+   (forall x y. P3(x) /\ P4(y) ==> R(x,y)) /\
+   (forall x y. P3(x) /\ P5(y) ==> ~(R(x,y))) /\
+   (forall x. (P4(x) \/ P5(x)) ==> exists y. Q0(y) /\ R(x,y))
+   ==> exists x y. P0(x) /\ P0(y) /\
+                   exists z. Q1(z) /\ R(y,z) /\ R(x,y)>>;;
+
+*************** -}
+
+
+-- ------------------------------------------------------------------------- 
+-- Test it.                                                                  
+-- ------------------------------------------------------------------------- 
+
+let prop_1 = time meson
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let prop_2 = time meson
+ <<~ ~p <=> p>>;;
+
+let prop_3 = time meson
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let prop_4 = time meson
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let prop_5 = time meson
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let prop_6 = time meson
+ <<p \/ ~p>>;;
+
+let prop_7 = time meson
+ <<p \/ ~ ~ ~p>>;;
+
+let prop_8 = time meson
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let prop_9 = time meson
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let prop_10 = time meson
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let prop_11 = time meson
+ <<p <=> p>>;;
+
+let prop_12 = time meson
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let prop_13 = time meson
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let prop_14 = time meson
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let prop_15 = time meson
+ <<p ==> q <=> ~p \/ q>>;;
+
+let prop_16 = time meson
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let prop_17 = time meson
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time meson
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time meson
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time meson
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time meson
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time meson
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time meson
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time meson
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time meson
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time meson
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time meson
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time meson
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time meson
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time meson
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time meson
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time meson
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time meson
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time meson
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time meson
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time meson
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time meson
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time meson
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+let p39 = time meson
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time meson
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time meson
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time meson
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+let p43 = time meson
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+let p44 = time meson
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+let p45 = time meson
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+let p46 = time meson
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time meson
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time meson
+ <<P(f((a),b),f(b,c)) /\
+  P(f(b,c),f(a,c)) /\
+  (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+  ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time meson
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time meson
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time meson
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- ** Amazingly, this still seems non-trivial... in HOL it works at depth 45!
+
+let gilmore_1 = time meson
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+ ** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time meson
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time meson
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time meson
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time meson
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time meson
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This is still a very hard problem
+
+let gilmore_9 = time meson
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Translation of Gilmore procedure using separate definitions.              
+-- ------------------------------------------------------------------------- 
+
+let gilmore_9a = time meson
+ <<(forall x y. P(x,y) <=>
+                forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+   ==> forall x. exists y. forall z.
+             (P(y,x) ==> (P(x,z) ==> P(x,y))) /\
+             (P(x,y) ==> (~P(x,z) ==> P(y,x) /\ P(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time meson
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The "connections make things worse" example once again.                   
+-- ------------------------------------------------------------------------- 
+
+meson <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+        (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+        (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Normal.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Normal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Normal.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Versions of the normal form functions in Prop for FirstOrderFormula.
+module Data.Logic.Harrison.Normal
+    ( trivial
+    , simpdnf
+    , simpdnf'
+    , simpcnf
+    , simpcnf'
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fromFirstOrder)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Failing (failing)
+import Data.Logic.Harrison.Lib (allpairs, setAny)
+import Data.Logic.Harrison.Skolem (nnf)
+import qualified Data.Set.Extra as Set (distrib, empty, filter, intersection, isProperSubsetOf, map, null, or, partition, Set, singleton, union)
+import Prelude hiding (negate)
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.  (dsf: now set)
+-- ------------------------------------------------------------------------- 
+
+distrib' :: (Eq formula, Ord formula) => Set.Set (Set.Set formula) -> Set.Set (Set.Set formula) -> Set.Set (Set.Set formula)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (neg, pos) = Set.partition negated lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: (FirstOrderFormula fof atom v, Eq fof, Ord fof) =>
+           fof -> Set.Set (Set.Set fof)
+simpdnf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at _ = Set.singleton (Set.singleton fm)
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf (nnf fm))
+
+purednf :: (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purednf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = Set.singleton (Set.singleton fm)
+      co (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      co (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      co _ = Set.singleton (Set.singleton fm)
+      tf = Set.singleton . Set.singleton . fromBool
+      at _ = Set.singleton (Set.singleton fm)
+
+simpdnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Formula lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+simpdnf' fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at = Set.singleton . Set.singleton . atomic
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf' (nnf fm))
+
+purednf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+purednf' fm =
+    foldFirstOrder (\ _ _ _ -> x) co (\ _ -> x) (\ _ -> x)  fm
+    where
+      -- co :: Combination formula -> Set.Set (Set.Set lit)
+      co (BinOp p (:&:) q) = Set.distrib (purednf' p) (purednf' q)
+      co (BinOp p (:|:) q) = Set.union (purednf' p) (purednf' q)
+      co _ = x
+      -- x :: Set.Set (Set.Set lit)
+      x = failing (const (error "purednf'")) (Set.singleton . Set.singleton) (fromFirstOrder id fm)
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+-- It would be nice to share code this way, but the caller needs to
+-- specify the intermediate lit type, which is a pain.
+-- simpcnf :: forall fof lit atom v. (FirstOrderFormula fof atom v, Ord fof, Literal lit atom v, Eq lit, Ord lit) => fof -> Set.Set (Set.Set fof)
+-- simpcnf fm = Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+
+simpcnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+simpcnf fm =
+    -- Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      at x = Set.singleton (Set.singleton (atomic x))
+      -- Discard any clause that is the proper subset of another clause
+      def = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+
+purecnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purecnf fm = Set.map (Set.map ({-simplify .-} (.~.))) (purednf (nnf ((.~.) fm)))
+
+-- Alternative versions, these should be merged
+
+simpcnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+simpcnf' fm =
+    foldFirstOrder (\ _ _ _ -> cjs') co tf at fm
+    where
+      co _ = cjs'
+      at = Set.singleton . Set.singleton . atomic -- foldAtomEq (\ _ _ -> cjs') tf (\ _ _ -> cjs')
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (`Set.isProperSubsetOf` x) cjs))
+      cjs = Set.filter (not . trivial) (purecnf' (nnf fm)) -- :: Set.Set (Set.Set lit)
+
+-- | CNF: (a | b | c) & (d | e | f)
+purecnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+purecnf' fm = Set.map (Set.map (.~.)) (purednf' (nnf ((.~.) fm)))
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Prolog.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Prolog.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Prolog.hs
@@ -0,0 +1,194 @@
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Prolog where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Term (Term(vt))
+import Data.String (IsString (fromString))
+import Data.Logic.Harrison.FOL (fv, subst, list_conj)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- ========================================================================= 
+-- Backchaining procedure for Horn clauses, and toy Prolog implementation.   
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Rename a rule.                                                            
+-- ------------------------------------------------------------------------- 
+
+renamerule :: forall fof atom term v f. (FirstOrderFormula fof atom v, {-Formula fof term v,-} Atom atom term v, Term term v f, Ord fof) =>
+              Int -> (Set.Set fof, fof) -> ((Set.Set fof, fof), Int)
+renamerule k (asm,c) =
+    ((Set.map inst asm, inst c), k + Set.size fvs)
+    where
+      fvs = fv (list_conj (Set.insert c asm)) :: Set.Set v
+      vvs = Map.fromList (map (\ (v, i) -> (v, vt (fromString ("_" ++ show i)))) (zip (Set.toList fvs) [k..])) :: Map.Map v term
+      inst = subst vvs :: fof -> fof
+
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Basic prover for Horn clauses based on backchaining with unification.     *)
+(* ------------------------------------------------------------------------- *)
+
+let rec backchain rules n k env goals =
+  match goals with
+    [] -> env
+  | g::gs ->
+     if n = 0 then failwith "Too deep" else
+     tryfind (fun rule ->
+        let (a,c),k' = renamerule k rule in
+        backchain rules (n - 1) k' (unify_literals env (c,g)) (a @ gs))
+     rules;;
+
+let hornify cls =
+  let pos,neg = partition positive cls in
+  if length pos > 1 then failwith "non-Horn clause"
+  else (map negate neg,if pos = [] then False else hd pos);;
+
+let hornprove fm =
+  let rules = map hornify (simpcnf(skolemize(Not(generalize fm)))) in
+  deepen (fun n -> backchain rules n 0 undefined [False],n) 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* A Horn example.                                                           *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let p32 = hornprove
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x))
+   ==> (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* A non-Horn example.                                                       *)
+(* ------------------------------------------------------------------------- *)
+
+(****************
+
+hornprove <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+**********)
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* Parsing rules in a Prolog-like syntax.                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let parserule s =
+  let c,rest =
+    parse_formula (parse_infix_atom,parse_atom) [] (lex(explode s)) in
+  let asm,rest1 =
+    if rest <> [] & hd rest = ":-"
+    then parse_list ","
+          (parse_formula (parse_infix_atom,parse_atom) []) (tl rest)
+    else [],rest in
+  if rest1 = [] then (asm,c) else failwith "Extra material after rule";;
+
+(* ------------------------------------------------------------------------- *)
+(* Prolog interpreter: just use depth-first search not iterative deepening.  *)
+(* ------------------------------------------------------------------------- *)
+
+let simpleprolog rules gl =
+  backchain (map parserule rules) (-1) 0 undefined [parse gl];;
+
+(* ------------------------------------------------------------------------- *)
+(* Ordering example.                                                         *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let lerules = ["0 <= X"; "S(X) <= S(Y) :- X <= Y"];;
+
+simpleprolog lerules "S(S(0)) <= S(S(S(0)))";;
+
+(*** simpleprolog lerules "S(S(0)) <= S(0)";;
+ ***)
+
+let env = simpleprolog lerules "S(S(0)) <= X";;
+apply env "X";;
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* With instantiation collection to produce a more readable result.          *)
+(* ------------------------------------------------------------------------- *)
+
+let prolog rules gl =
+  let i = solve(simpleprolog rules gl) in
+  mapfilter (fun x -> Atom(R("=",[Var x; apply i x]))) (fv(parse gl));;
+
+(* ------------------------------------------------------------------------- *)
+(* Example again.                                                            *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+prolog lerules "S(S(0)) <= X";;
+
+(* ------------------------------------------------------------------------- *)
+(* Append example, showing symmetry between inputs and outputs.              *)
+(* ------------------------------------------------------------------------- *)
+
+let appendrules =
+  ["append(nil,L,L)"; "append(H::T,L,H::A) :- append(T,L,A)"];;
+
+prolog appendrules "append(1::2::nil,3::4::nil,Z)";;
+
+prolog appendrules "append(1::2::nil,Y,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,3::4::nil,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,Y,1::2::3::4::nil)";;
+
+(* ------------------------------------------------------------------------- *)
+(* However this way round doesn't work.                                      *)
+(* ------------------------------------------------------------------------- *)
+
+(***
+ *** prolog appendrules "append(X,3::4::nil,X)";;
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* A sorting example (from Lloyd's "Foundations of Logic Programming").      *)
+(* ------------------------------------------------------------------------- *)
+
+let sortrules =
+ ["sort(X,Y) :- perm(X,Y),sorted(Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+prolog sortrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+(* ------------------------------------------------------------------------- *)
+(* Yet with a simple swap of the first two predicates...                     *)
+(* ------------------------------------------------------------------------- *)
+
+let badrules =
+ ["sort(X,Y) :- sorted(Y), perm(X,Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+(*** This no longer works
+
+prolog badrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+ ***)
+END_INTERACTIVE;;                           
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Prop.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Prop.hs
@@ -0,0 +1,451 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Prop
+    ( eval
+    , atoms
+    , onAllValuations
+    , TruthTable
+    , TruthTableRow
+    , truthTable
+    , tautology
+    , unsatisfiable
+    , satisfiable
+    , rawdnf
+    , purednf
+    , dnf
+    , dnf'
+    , trivial
+    , psimplify
+    , nnf
+    , simpdnf
+    , simpcnf
+    , positive
+    , negative
+    , negate
+    , distrib
+    , list_disj
+    , list_conj
+    -- previously unexported
+    , pSubst
+    , dual
+    , nenf
+    , mkLits
+    , allSatValuations
+    , dnf0
+    , cnf
+    , cnf'
+    ) where
+
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, ifElse, true)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Literal.ToPropositional (toPropositional)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Formulas.Propositional (atom_union, on_atoms)
+import Data.Logic.Harrison.Lib (distrib', fpf, setAny)
+import qualified Data.Map as Map (empty, findWithDefault, insert, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, intersection, isProperSubsetOf, map, minView, null, partition, Set, singleton, toAscList, union)
+import Prelude hiding (negate)
+
+-- type Map a = Map.Map a Bool
+-- m0 = Map.empty
+-- ins :: forall a. Ord a => a -> Bool -> Map a -> Map a
+-- ins = Map.insert
+-- m ! k = Map.findWithDefault False k m
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of propositional formulas.                                        
+-- ------------------------------------------------------------------------- 
+
+{-
+let parse_propvar vs inp =
+  match inp with
+    p::oinp when p /= "(" -> Atom(P(p)),oinp
+  | _ -> failwith "parse_propvar";;
+
+let parse_prop_formula = make_parser
+  (parse_formula ((fun _ _ -> failwith ""),parse_propvar) []);;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Set this up as default for quotations.                                    
+-- ------------------------------------------------------------------------- 
+
+{-
+let default_parser = parse_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printer.                                                                  
+-- ------------------------------------------------------------------------- 
+
+{-
+let print_propvar prec p = print_string(pname p);;
+
+let print_prop_formula = print_qformula print_propvar;;
+
+#install_printer print_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Map.Map atomic Bool -> Bool
+eval fm v =
+    foldPropositional co id at fm
+    where
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at x = Map.findWithDefault False x v
+
+{-
+START_INTERACTIVE;;
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> false | P"r" -> true);;
+
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> true | P"r" -> false);;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Return the set of propositional variables in a formula.                   
+-- ------------------------------------------------------------------------- 
+
+atoms :: Ord atomic => PropositionalFormula formula atomic => formula -> Set.Set atomic
+atoms = atom_union Set.singleton
+
+-- ------------------------------------------------------------------------- 
+-- Code to print out truth tables.                                           
+-- ------------------------------------------------------------------------- 
+
+onAllValuations :: (Ord a) =>
+                   (r -> r -> r)         -- ^ Combine function for result type
+                -> (Map.Map a Bool -> r) -- ^ The substitution function
+                -> Map.Map a Bool        -- ^ The default valuation function for atoms not in ps
+                -> Set.Set a             -- ^ The variables to vary
+                -> r
+onAllValuations _ subfn v ps | Set.null ps = subfn v
+onAllValuations append subfn v ps =
+    case Set.minView ps of
+      Nothing -> error "onAllValuations"
+      Just (p, ps') ->
+          append -- Do the valuations of the remaining variables with  set to false
+                 (onAllValuations append subfn (Map.insert p False v) ps')
+                 -- Do the valuations of the remaining variables with  set to true
+                 (onAllValuations append subfn (Map.insert p True v) ps')
+
+type TruthTableRow = ([Bool], Bool)
+type TruthTable a = ([a], [TruthTableRow])
+
+truthTable :: forall formula atom. (PropositionalFormula formula atom, Eq atom, Ord atom) =>
+              formula -> TruthTable atom
+truthTable fm =
+    (atl, onAllValuations (++) mkRow Map.empty ats)
+    where
+      mkRow :: Map.Map atom Bool      -- ^ The current variable assignment
+            -> [TruthTableRow]          -- ^ The variable assignments and the formula value
+      mkRow v = [(map (\ k -> Map.findWithDefault False k v) atl, eval fm v)]
+      atl = Set.toAscList ats
+      ats = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- Recognizing tautologies.                                                  
+-- ------------------------------------------------------------------------- 
+
+tautology :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+tautology fm = onAllValuations (&&) (eval fm) Map.empty (atoms fm)
+
+-- ------------------------------------------------------------------------- 
+-- Related concepts.                                                         
+-- ------------------------------------------------------------------------- 
+
+
+unsatisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+unsatisfiable fm = tautology ((.~.) fm)
+
+satisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+satisfiable = not . unsatisfiable
+
+-- ------------------------------------------------------------------------- 
+-- Substitution operation.                                                   
+-- ------------------------------------------------------------------------- 
+
+-- pSubst :: Ord a => Map.Map a (Formula a) -> Formula a -> Formula a
+pSubst :: (PropositionalFormula formula atomic, Ord atomic) => Map.Map atomic formula -> formula -> formula
+pSubst subfn fm = on_atoms (\ p -> maybe (atomic p) id (fpf subfn p)) fm
+
+-- ------------------------------------------------------------------------- 
+-- Dualization.                                                              
+-- ------------------------------------------------------------------------- 
+
+dual :: forall formula atomic. (PropositionalFormula formula atomic) => formula -> formula
+dual fm =
+    foldPropositional co (fromBool . not) at fm
+    where
+      co ((:~:) _) = fm
+      co (BinOp p (:&:) q) = dual p .|. dual q
+      co (BinOp p (:|:) q) = dual p .&. dual q
+      co _ = error "dual: Formula involves connectives ==> or <=>";;
+      at = atomic
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+psimplify1 :: (PropositionalFormula r a, Eq r) => r -> r
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) fm') = foldPropositional simplifyNotCombine (fromBool . not) (\ _ -> fm) fm'
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _         ) -> r
+            (Just False, (:&:), _         ) -> false
+            (_,          (:&:), Just True ) -> l
+            (_,          (:&:), Just False) -> false
+            (Just True,  (:|:), _         ) -> true
+            (Just False, (:|:), _         ) -> r
+            (_,          (:|:), Just True ) -> true
+            (_,          (:|:), Just False) -> l
+            (Just True,  (:=>:), _         ) -> r
+            (Just False, (:=>:), _         ) -> true
+            (_,          (:=>:), Just True ) -> true
+            (_,          (:=>:), Just False) -> (.~.) l
+            (Just True,  (:<=>:), _         ) -> r
+            (Just False, (:<=>:), _         ) -> (.~.) r
+            (_,          (:<=>:), Just True ) -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _ -> fm
+
+      simplifyNotCombine ((:~:) p) = p
+      simplifyNotCombine _ = fm
+
+psimplify :: forall formula atomic. (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+psimplify fm =
+    foldPropositional c (\ _ -> fm) (\ _ -> fm) fm
+    where
+      c :: Combination formula -> formula
+      c ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      c (BinOp p op q) = psimplify1 (binop (psimplify p) op (psimplify q))
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.                                              
+-- ------------------------------------------------------------------------- 
+
+negative :: forall lit atom. Literal lit atom => lit -> Bool
+negative lit =
+    foldLiteral neg tf a lit
+    where
+      neg _ = True
+      tf = not
+      a _ = False
+
+positive :: Literal lit atom => lit -> Bool
+positive = not . negative
+
+negate :: PropositionalFormula formula atomic => formula -> formula
+negate lit =
+    foldPropositional c (fromBool . not) a lit
+    where
+      c ((:~:) p) = p
+      c _ = (.~.) lit
+      a _ = (.~.) lit
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf' :: PropositionalFormula formula atomic => formula -> formula
+nnf' fm =
+    foldPropositional nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfCombine ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf' ((.~.) p) .|. (nnf' q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf' p .&. nnf' q) .|. (nnf' ((.~.) p) .&. nnf' ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf' p .&. nnf' q
+      nnfCombine (BinOp p (:|:) q) = nnf' p .|. nnf' q
+      nnfNotCombine ((:~:) p) = nnf' p
+      nnfNotCombine (BinOp p (:&:) q) = nnf' ((.~.) p) .|. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf' ((.~.) p) .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf' p .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' ((.~.) q)) .|. nnf' ((.~.) p) .&. nnf' q
+
+-- ------------------------------------------------------------------------- 
+-- Roll in simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+nnf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nnf = nnf' . psimplify
+
+-- ------------------------------------------------------------------------- 
+-- Simple negation-pushing when we don't care to distinguish occurrences.    
+-- ------------------------------------------------------------------------- 
+
+nenf' :: PropositionalFormula formula atomic => formula -> formula
+nenf' fm =
+    foldPropositional nenfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nenfCombine ((:~:) p) = foldPropositional nenfNotCombine (\ _ -> fm) (\ _ -> fm) p
+      nenfCombine (BinOp p (:&:) q) = nenf' p .&. nenf' q
+      nenfCombine (BinOp p (:|:) q) = nenf' p .|. nenf' q
+      nenfCombine (BinOp p (:=>:) q) = nenf' ((.~.) p) .|. nenf' q
+      nenfCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' q
+      nenfNotCombine ((:~:) p) = p
+      nenfNotCombine (BinOp p (:&:) q) = nenf' ((.~.) p) .|. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:|:) q) = nenf' ((.~.) p) .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:=>:) q) = nenf' p .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' ((.~.) q) -- really?  how is this asymmetrical?
+
+nenf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nenf = nenf' . psimplify
+
+{-
+# Not (prime 2) ->
+  <<~(~(((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1))>>
+
+# nenf (Not (prime 2)) -> 
+  <<((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1>>
+
+> pretty ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N)))
+     (out0 ⇔ x0 ∧ y0) ∧ ¬out1 ∧ out1 ∧ ¬out0
+
+> pretty (nenf ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N))))
+     (out0 ⇔ x0 ∨ y0) ∨ ¬out1 ∨ out1 ∨ ¬out0
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Disjunctive normal form (DNF) via truth tables.                           
+-- ------------------------------------------------------------------------- 
+
+list_conj :: (PropositionalFormula formula atomic, Ord formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: PropositionalFormula formula atomic => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (PropositionalFormula formula atomic, Ord formula, Ord atomic) =>
+          Set.Set formula -> Map.Map atomic Bool -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+allSatValuations :: Ord a => (Map.Map a Bool -> Bool) -> Map.Map a Bool -> Set.Set a -> [Map.Map a Bool]
+allSatValuations subfn v pvs =
+    case Set.minView pvs of
+      Nothing -> if subfn v then [v] else []
+      Just (p, ps) -> (allSatValuations subfn (Map.insert p False v) ps) ++
+                      (allSatValuations subfn (Map.insert p True v) ps)
+
+dnf0 :: forall formula atomic. (PropositionalFormula formula atomic, Ord atomic, Ord formula) => formula -> formula
+dnf0 fm =
+    list_disj (Set.fromList (map (mkLits (Set.map atomic pvs)) satvals))
+    where
+      satvals = allSatValuations (eval fm) Map.empty pvs
+      pvs = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- DNF via distribution.                                                     
+-- ------------------------------------------------------------------------- 
+
+distrib :: PropositionalFormula formula atomic => formula -> formula
+distrib fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) s) =
+          foldPropositional c' tf a s
+          where c' (BinOp q (:|:) r) = distrib (p .&. q) .|. distrib (p .&. r)
+                c' _ =
+                    foldPropositional c'' tf a p
+                    where c'' (BinOp q (:|:) r) = distrib (q .&. s) .|. distrib (r .&. s)
+                          c'' _ = fm
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+rawdnf :: PropositionalFormula formula atomic => formula -> formula
+rawdnf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib (rawdnf p .&. rawdnf q)
+      c (BinOp p (:|:) q) = rawdnf p .|. rawdnf q
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.                                    
+-- ------------------------------------------------------------------------- 
+
+purednf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purednf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c ((:~:) p) = Set.map (Set.map (.~.)) (purednf p)
+      c _ = error "purednf" -- Set.singleton (Set.singleton fm)
+      tf x = Set.singleton (Set.singleton (fromBool x))
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Literal lit atom, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (pos, neg) = Set.partition positive lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpdnf fm =
+    foldPropositional c tf a fm
+    where
+      c :: Combination pf -> Set.Set (Set.Set lit)
+      c _ = Set.filter (\ d -> not (setAny (\ d' -> Set.isProperSubsetOf d' d) djs)) djs
+          where djs = Set.filter (not . trivial) (purednf (nnf fm))
+      tf = ifElse (Set.singleton Set.empty) Set.empty
+      a :: atom -> Set.Set (Set.Set lit)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Mapping back to a formula.                                                
+-- ------------------------------------------------------------------------- 
+
+dnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+dnf = list_disj . Set.map (list_conj . Set.map (toPropositional id))
+
+dnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+dnf' = dnf . (simpdnf :: pf -> Set.Set (Set.Set pf))
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+purecnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+simpcnf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpcnf fm =
+    foldPropositional c tf a fm
+    where
+      tf = ifElse Set.empty (Set.singleton Set.empty)
+      -- Discard any clause that is the proper subset of another clause
+      c _ = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+cnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+cnf = list_conj . Set.map (list_disj . Set.map (toPropositional id))
+
+cnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+cnf' = cnf . (simpcnf :: pf -> Set.Set (Set.Set pf))
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/PropExamples.hs b/testdata/split-merge-expected/Data/Logic/Harrison/PropExamples.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/PropExamples.hs
@@ -0,0 +1,392 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+module Data.Logic.Harrison.PropExamples
+    ( Atom(..)
+    , N
+    , prime
+    , ramsey
+    , tests
+    ) where
+
+import Data.Bits (Bits, shiftR)
+import Data.Logic.Classes.Combine ((.<=>.), (.=>.), (.&.), (.|.), Combinable, Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (true, false)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), botFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Lib (allsets)
+import Data.Logic.Harrison.Prop (tautology, list_conj, list_disj, psimplify)
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Set as Set
+import Prelude hiding (sum)
+import Test.HUnit
+import Text.PrettyPrint (text)
+
+tests :: Test
+tests = TestList [test01, test02, test03]
+
+-- ========================================================================= 
+-- Some propositional formulas to test, and functions to generate classes.   
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Generate assertion equivalent to R(s,t) <= n for the Ramsey number R(s,t) 
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P String a (Maybe a) deriving (Eq, Ord, Show)
+
+instance Pretty (Atom N) where
+    pretty (P s n mm) = text (s ++ show n ++ maybe "" (\ m -> "." ++ show m) mm)
+
+instance HasFixity (Atom N) where
+    fixity = const botFixity
+
+type N = Int
+
+ramsey :: forall formula.
+          (PropositionalFormula formula (Atom N), Ord formula) =>
+          Int -> Int -> N -> formula
+ramsey s t n =
+  let vertices = Set.fromList [1 .. n] in
+  let yesgrps = Set.map (allsets (2 :: Int)) (allsets s vertices)
+      nogrps = Set.map (allsets (2 :: Int)) (allsets t vertices) in
+  let e xs = let [m, n] = Set.toAscList xs in C.atomic (P "p" m (Just n)) in
+  list_disj (Set.map (list_conj . Set.map e) yesgrps) .|. list_disj (Set.map (list_conj . Set.map (\ p -> (.~.)(e p))) nogrps)
+
+-- ------------------------------------------------------------------------- 
+-- Some currently tractable examples.                                        
+-- ------------------------------------------------------------------------- 
+
+test01 :: Test
+test01 = TestList [{- TestCase (assertEqual "ramsey 3 3 4"
+                                             (Combine
+                                              (BinOp
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp
+                                                   (Atom (P "p" 1 (Just 4)))
+                                                   (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 2 (Just 4)))
+                                                     (:&:)
+                                                     (Atom (P "p" 1 (Just 2)))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 1 (Just 4)))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 3 (Just 4)))
+                                                       (:&:)
+                                                       (Atom (P "p" 1 (Just 3)))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 2 (Just 4)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 3 (Just 4)))
+                                                         (:&:)
+                                                         (Atom (P "p" 2 (Just 3)))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 1 (Just 3)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 2 (Just 3)))
+                                                         (:&:)
+                                                         (Atom (P "p" 1 (Just 2)))))))))))))
+                                               (:|:)
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 4))))) (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 1 (Just 2)))))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp (Combine
+                                                            ((:~:) (Atom (P "p" 1 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 2)))))))))))))))))
+                                         (ramsey 3 3 4 :: Formula (Atom N))), -}
+                   TestCase (assertEqual "tautology (ramsey 3 3 5)" False (tautology (ramsey 3 3 5 :: Formula (Atom N)))),
+                   TestCase (assertEqual "tautology (ramsey 3 3 6)" True (tautology (ramsey 3 3 6 :: Formula (Atom N))))]
+
+-- ------------------------------------------------------------------------- 
+-- Half adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+halfsum :: forall formula. Combinable formula => formula -> formula -> formula
+halfsum x y = x .<=>. ((.~.) y)
+
+halfcarry :: forall formula. Combinable formula => formula -> formula -> formula
+halfcarry x y = x .&. y
+
+ha :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula
+ha x y s c = (s .<=>. halfsum x y) .&. (c .<=>. halfcarry x y)
+
+-- ------------------------------------------------------------------------- 
+-- Full adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+carry :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+carry x y z = (x .&. y) .|. ((x .|. y) .&. z)
+
+sum :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+sum x y z = halfsum (halfsum x y) z
+
+fa :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula -> formula
+fa x y z s c = (s .<=>. sum x y z) .&. (c .<=>. carry x y z)
+
+-- ------------------------------------------------------------------------- 
+-- Useful idiom.                                                             
+-- ------------------------------------------------------------------------- 
+
+conjoin :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a) => (a -> formula) -> Set.Set a -> formula
+conjoin f l = list_conj (Set.map f l)
+
+-- ------------------------------------------------------------------------- 
+-- n-bit ripple carry adder with carry c(0) propagated in and c(n) out.      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> formula
+ripplecarry x y c out n =
+    conjoin (\ i -> fa (x i) (y i) (c i) (out i) (c(i + 1))) (Set.fromList [0 .. (n - 1)])
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+mk_index :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> formula
+mk_index x i = C.atomic (P x i Nothing)
+mk_index2 :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> a -> formula
+mk_index2 x i j = C.atomic (P x i (Just j))
+
+test02 = TestCase (assertEqual "ripplecarry x y c out 2"
+                               (Combine (BinOp (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 1 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 1 Nothing)))))))) (:&:)
+                                                         (Combine (BinOp (Atom (P "C" 2 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:&:) (Atom (P "Y" 1 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:|:) (Atom (P "Y" 1 Nothing)))) (:&:) (Atom (P "C" 1 Nothing)))))))))) (:&:)
+                                         (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 0 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 0 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 0 Nothing)))))))) (:&:)
+                                                   (Combine (BinOp (Atom (P "C" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:&:) (Atom (P "Y" 0 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:|:) (Atom (P "Y" 0 Nothing)))) (:&:) (Atom (P "C" 0 Nothing))))))))))))
+                               {- <<((OUT_0 <=> (X_0 <=> ~Y_0) <=> ~C_0) /\
+                                      (C_1 <=> X_0 /\ Y_0 \/ (X_0 \/ Y_0) /\ C_0)) /\
+                                     (OUT_1 <=> (X_1 <=> ~Y_1) <=> ~C_1) /\
+                                     (C_2 <=> X_1 /\ Y_1 \/ (X_1 \/ Y_1) /\ C_1)>> -}
+                               (let [x, y, out, c] = map mk_index ["X", "Y", "OUT", "C"] in
+                                ripplecarry x y c out 2 :: Formula (Atom N)))
+
+-- ------------------------------------------------------------------------- 
+-- Special case with 0 instead of c(0).                                      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry0 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry0 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then false else c i) out n)
+
+-- ------------------------------------------------------------------------- 
+-- Carry-select adder                                                        
+-- ------------------------------------------------------------------------- 
+
+ripplecarry1 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry1 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then true else c i) out n)
+
+mux :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+mux sel in0 in1 = (((.~.) sel) .&. in0) .|. (sel .&. in1)
+
+offset :: forall t a. Num a => a -> (a -> t) -> a -> t
+offset n x i = x (n + i)
+
+carryselect :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> a -> formula
+carryselect x y c0 c1 s0 s1 c s n k =
+  let k' = min n k in
+  let fm = ((ripplecarry0 x y c0 s0 k') .&. (ripplecarry1 x y c1 s1 k')) .&.
+           (((c k') .<=>. (mux (c 0) (c0 k') (c1 k'))) .&.
+            (conjoin (\ i -> (s i) .<=>. (mux (c 0) (s0 i) (s1 i)))
+                             (Set.fromList [0 .. (k' - 1)]))) in
+  if k' < k then fm else
+  fm .&. (carryselect
+          (offset k x) (offset k y) (offset k c0) (offset k c1)
+          (offset k s0) (offset k s1) (offset k c) (offset k s)
+          (n - k) k)
+
+-- ------------------------------------------------------------------------- 
+-- Equivalence problems for carry-select vs ripple carry adders.             
+-- ------------------------------------------------------------------------- 
+
+mk_adder_test :: forall formula a. (PropositionalFormula formula (Atom a), Ord a, Ord formula, Num a, Enum a) =>
+                 a -> a -> formula
+mk_adder_test n k =
+  let [x, y, c, s, c0, s0, c1, s1, c2, s2] =
+          map mk_index ["x", "y", "c", "s", "c0", "s0", "c1", "s1", "c2", "s2"] in
+  (((carryselect x y c0 c1 s0 s1 c s n k) .&.
+    ((.~.) (c 0))) .&.
+   (ripplecarry0 x y c2 s2 n)) .=>.
+  (((c n) .<=>. (c2 n)) .&.
+   (conjoin (\ i -> (s i) .<=>. (s2 i)) (Set.fromList [0 .. (n - 1)])))
+
+-- ------------------------------------------------------------------------- 
+-- Ripple carry stage that separates off the final result.                   
+--                                                                           
+--       UUUUUUUUUUUUUUUUUUUU  (u)                                           
+--    +  VVVVVVVVVVVVVVVVVVVV  (v)                                           
+--                                                                           
+--    = WWWWWWWWWWWWWWWWWWWW   (w)                                           
+--    +                     Z  (z)                                           
+-- ------------------------------------------------------------------------- 
+
+rippleshift :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> formula
+            -> (a -> formula)
+            -> a -> formula
+rippleshift u v c z w n =
+  ripplecarry0 u v (\ i -> if i == n then w(n - 1) else c(i + 1))
+                   (\ i -> if i == 0 then z else w(i - 1)) n
+-- ------------------------------------------------------------------------- 
+-- Naive multiplier based on repeated ripple carry.                          
+-- ------------------------------------------------------------------------- 
+
+multiplier :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+              (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> formula)
+           -> a
+           -> formula
+multiplier x u v out n =
+  if n == 1 then ((out 0) .<=>. (x 0 0)) .&. ((.~.)(out 1)) else
+  psimplify (((out 0) .<=>. (x 0 0)) .&.
+             ((rippleshift
+               (\ i -> if i == n - 1 then false else x 0 (i + 1))
+               (x 1) (v 2) (out 1) (u 2) n) .&.
+              (if n == 2 then ((out 2) .<=>. (u 2 0)) .&. ((out 3) .<=>. (u 2 1)) else
+                   conjoin (\ k -> rippleshift (u k) (x k) (v(k + 1)) (out k)
+                                   (if k == n - 1 then \ i -> out(n + i)
+                                    else u(k + 1)) n) (Set.fromList [2 .. (n - 1)]))))
+
+-- ------------------------------------------------------------------------- 
+-- Primality examples.                                                       
+-- For large examples, should use "num" instead of "int" in these functions. 
+-- ------------------------------------------------------------------------- 
+
+bitlength :: forall b a. (Num a, Num b, Bits b) => b -> a
+bitlength x = if x == 0 then 0 else 1 + bitlength (shiftR x 1);;
+
+bit :: forall a b. (Num a, Eq a, Bits b, Integral b) => a -> b -> Bool
+bit n x = if n == 0 then x `mod` 2 == 1 else bit (n - 1) (shiftR x 1)
+
+congruent_to :: forall formula atomic a b. (Bits b, PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Integral b, Enum a) =>
+                (a -> formula) -> b -> a -> formula
+congruent_to x m n =
+  conjoin (\ i -> if bit i m then x i else (.~.)(x i))
+          (Set.fromList [0 .. (n - 1)])
+
+prime :: forall formula. (PropositionalFormula formula (Atom N), Ord formula) => N -> formula
+prime p =
+  let [x, y, out] = map mk_index ["x", "y", "out"] in
+  let m i j = (x i) .&. (y j)
+      [u, v] = map mk_index2 ["u", "v"] in
+  let (n :: Int) = bitlength p in
+  (.~.) (multiplier m u v out (n - 1) .&. congruent_to out p (max n (2 * n - 2)))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+type F = Formula (Atom Int)
+
+deriving instance Show F
+
+{-
+instance Constants F where
+    fromBool True = 
+-}
+
+test03 :: Test
+test03 =
+    TestList [TestCase (assertEqual "tautology(prime 7)" True (tautology(prime 7 :: F))),
+              TestCase (assertEqual "tautology(prime 9)" False (tautology(prime 9 :: F))),
+              TestCase (assertEqual "tautology(prime 11)" True (tautology(prime 11 :: F)))]
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Resolution.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Resolution.hs
@@ -0,0 +1,1044 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Resolution
+    ( resolution1
+    , resolution2
+    , resolution3
+    , presolution
+    , matchAtomsEq
+    ) where
+
+import Data.Logic.Classes.Atom (Atom(match))
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), zipFirstOrder)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(vt, foldTerm))
+import Data.Logic.Classes.Variable (Variable(prefix))
+import Data.Logic.Failing (failing, Failing(..))
+import Data.Logic.Harrison.FOL (fv, generalize, list_conj, list_disj, subst)
+import Data.Logic.Harrison.Lib (allnonemptysubsets, allpairs, allsubsets, apply, defined, setAll, setAny, settryfind, (|->))
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf, trivial)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (unify_literals)
+import Data.Logic.Harrison.Unif (solve)
+import qualified Data.Map as Map (empty, fromList, Map)
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set (difference, empty, filter, fold, insert, map, member, minView, null, partition, Set, singleton, toList, union)
+
+-- ========================================================================= 
+-- Resolution.                                                               
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- MGU of a set of literals.                                                 
+-- ------------------------------------------------------------------------- 
+
+mgu :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+       Set.Set lit -> Map.Map v term -> Failing (Map.Map v term)
+mgu l env =
+    case Set.minView l of
+      Just (a, rest) ->
+          case Set.minView rest of
+            Just (b, _) -> unify_literals env a b >>= mgu rest
+            _ -> Success (solve env)
+      _ -> Success (solve env)
+
+unifiable :: (Literal lit atom, Term term v f, Atom atom term v) =>
+             lit -> lit -> Bool
+unifiable p q = failing (const False) (const True) (unify_literals Map.empty p q)
+
+-- ------------------------------------------------------------------------- 
+-- Rename a clause.                                                          
+-- ------------------------------------------------------------------------- 
+
+rename :: (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+          (v -> v) -> Set.Set fof -> Set.Set fof
+rename pfx cls =
+    Set.map (subst (Map.fromList (zip fvs vvs))) cls
+    where
+      -- fvs :: [v]
+      fvs = Set.toList (fv (list_disj cls))
+      -- vvs :: [term]
+      vvs = map (vt . pfx) fvs
+
+-- ------------------------------------------------------------------------- 
+-- General resolution rule, incorporating factoring as in Robinson's paper.  
+-- ------------------------------------------------------------------------- 
+
+resolvents :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+              Set.Set fof -> Set.Set fof -> fof -> Set.Set fof -> Set.Set fof
+resolvents cl1 cl2 p acc =
+    if Set.null ps2 then acc else Set.fold doPair acc pairs
+    where
+      doPair (s1,s2) sof =
+          case mgu (Set.union s1 (Set.map (.~.) s2)) Map.empty of
+            Success mp -> Set.union (Set.map (subst mp) (Set.union (Set.difference cl1 s1) (Set.difference cl2 s2))) sof
+            Failure _ -> sof
+      -- pairs :: Set.Set (Set.Set fof, Set.Set fof)
+      pairs = allpairs (,) (Set.map (Set.insert p) (allsubsets ps1)) (allnonemptysubsets ps2)
+      -- ps1 :: Set.Set fof
+      ps1 = Set.filter (\ q -> q /= p && unifiable p q) cl1
+      -- ps2 :: Set.Set fof
+      ps2 = Set.filter (unifiable ((.~.) p)) cl2
+
+resolve_clauses :: forall fof atom v term f.
+                   (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                   Set.Set fof -> Set.Set fof -> Set.Set fof
+resolve_clauses cls1 cls2 =
+    let cls1' = rename (prefix "x") cls1
+        cls2' = rename (prefix "y") cls2 in
+    Set.fold (resolvents cls1' cls2') Set.empty cls1'
+
+-- ------------------------------------------------------------------------- 
+-- Basic "Argonne" loop.                                                     
+-- ------------------------------------------------------------------------- 
+
+resloop1 :: forall atom v term f fof. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+resloop1 used unused =
+    maybe (Failure ["No proof found"]) step (Set.minView unused)
+    where
+      step (cl, ros) =
+          if Set.member Set.empty news then return True else resloop1 used' (Set.union ros news)
+          where
+            used' = Set.insert cl used
+            -- resolve_clauses is not in the Failing monad, so setmapfilter isn't appropriate.
+            news = Set.fold Set.insert Set.empty ({-setmapfilter-} Set.map (resolve_clauses cl) used')
+
+pure_resolution1 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution1 fm = resloop1 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution1 :: forall m fof term f atom v.
+               (Literal fof atom,
+                FirstOrderFormula fof atom v,
+                PropositionalFormula fof atom,
+                Term term v f,
+                Atom atom term v,
+                Ord fof,
+                Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution1 fm = askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution1 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Matching of terms and literals.                                           
+-- ------------------------------------------------------------------------- 
+
+term_match :: forall term v f. (Term term v f) => Map.Map v term -> [(term, term)] -> Failing (Map.Map v term)
+term_match env [] = Success env
+term_match env ((p, q) : oth) =
+    foldTerm v fn p
+    where
+      v x = if not (defined env x)
+            then term_match ((x |-> q) env) oth
+            else if apply env x == Just q
+                 then term_match env oth
+                 else Failure ["term_match"]
+      fn f fa =
+          foldTerm v' fn' q
+          where
+            fn' g ga | f == g && length fa == length ga = term_match env (zip fa ga ++ oth)
+            fn' _ _ = Failure ["term_match"]
+            v' _ = Failure ["term_match"]
+{-
+  case eqs of
+    [] -> Success env
+    (Fn f fa, Fn g ga) : oth
+        | f == g && length fa == length ga ->
+           term_match env (zip fa ga ++ oth)
+    (Var x, t) : oth ->
+        if not (defined env x) then term_match ((x |-> t) env) oth
+        else if apply env x == t then term_match env oth
+        else Failure ["term_match"]
+    _ -> Failure ["term_match"]
+-}
+
+match_literals :: forall term f v fof atom. (FirstOrderFormula fof atom v, Atom atom term v, Term term v f) =>
+                  Map.Map v term -> fof -> fof -> Failing (Map.Map v term)
+match_literals env t1 t2 =
+    fromMaybe err (zipFirstOrder qu co tf at t1 t2)
+    where
+      qu _ _ _ _ _ _ = Nothing
+      co ((:~:) p) ((:~:) q) = Just $ match_literals env p q
+      co _ _ = Nothing
+      tf a b = if a == b then Just (Success env) else Nothing
+      at a1 a2 = Just (match env a1 a2)
+      err = Failure ["match_literals"]
+
+-- Identical to unifyAtomsEq except calls term_match instead of unify.
+matchAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+matchAtomsEq env a1 a2 =
+    fromMaybe err (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p ts1 q ts2 =
+          if p == q && length ts1 == length ts2
+          then Just (term_match env (zip ts1 ts2))
+          else Nothing
+      tf p q = if p == q then Just (Success env) else Nothing
+      eq pl pr ql qr = Just (term_match env [(pl, ql), (pr, qr)])
+      err = Failure ["matchAtomsEq"]
+
+{-
+    case tmp of
+      (Atom (R p a1), Atom(R q a2)) -> term_match env [(Fn p a1, Fn q a2)]
+      (Not (Atom (R p a1)), Not (Atom (R q a2))) -> term_match env [(Fn p a1, Fn q a2)]
+      _ -> Failure ["match_literals"]
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Test for subsumption                                                      
+-- ------------------------------------------------------------------------- 
+
+subsumes_clause :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v) =>
+                   Set.Set fof -> Set.Set fof -> Bool
+subsumes_clause cls1 cls2 =
+    failing (const False) (const True) (subsume Map.empty cls1)
+    where
+      -- subsume :: Map.Map v term -> Set.Set fof -> Failing (Map.Map v term)
+      subsume env cls =
+          case Set.minView cls of
+            Nothing -> Success env
+            Just (l1, clt) -> settryfind (\ l2 -> case (match_literals env l1 l2) of
+                                                    Success env' -> subsume env' clt
+                                                    Failure msgs -> Failure msgs) cls2
+-- ------------------------------------------------------------------------- 
+-- With deletion of tautologies and bi-subsumption with "unused".            
+-- ------------------------------------------------------------------------- 
+
+replace :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set fof
+        -> Set.Set (Set.Set fof)
+        -> Set.Set (Set.Set fof)
+replace cl st =
+    case Set.minView st of
+      Nothing -> Set.singleton cl
+      Just (c, st') -> if subsumes_clause cl c
+                       then Set.insert cl st'
+                       else Set.insert c (replace cl st')
+
+incorporate :: forall fof term f v atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+               Set.Set fof
+            -> Set.Set fof
+            -> Set.Set (Set.Set fof)
+            -> Set.Set (Set.Set fof)
+incorporate gcl cl unused =
+    if trivial cl || setAny (\ c -> subsumes_clause c cl) (Set.insert gcl unused)
+    then unused
+    else replace cl unused
+
+resloop2 :: forall fof term f v atom. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof)
+         -> Set.Set (Set.Set fof)
+         -> Failing Bool
+resloop2 used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl {- :: Set.Set fof-}, ros {- :: Set.Set (Set.Set fof) -}) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = {-Set.fold Set.union Set.empty-} (Set.map (resolve_clauses cl) used') in
+          if Set.member Set.empty news then return True else resloop2 used' (Set.fold (incorporate cl) ros news)
+
+pure_resolution2 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution2 fm = resloop2 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution2 :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution2 fm = askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_resolution2 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Positive (P1) resolution.                                                 
+-- ------------------------------------------------------------------------- 
+
+presolve_clauses :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    Set.Set fof -> Set.Set fof -> Set.Set fof
+presolve_clauses cls1 cls2 =
+    if setAll positive cls1 || setAll positive cls2
+    then resolve_clauses cls1 cls2
+    else Set.empty
+
+presloop :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+presloop used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl, ros) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = Set.map (presolve_clauses cl) used' in
+          if Set.member Set.empty news
+          then Success True
+          else presloop used' (Set.fold (incorporate cl) ros news)
+
+pure_presolution :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_presolution fm = presloop Set.empty (simpcnf (specialize (pnf fm)))
+
+presolution :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+presolution fm =
+    askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_presolution . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Introduce a set-of-support restriction.                                   
+-- ------------------------------------------------------------------------- 
+
+pure_resolution3 :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution3 fm =
+    uncurry resloop2 (Set.partition (setAny positive) (simpcnf (specialize (pnf fm))))
+
+resolution3 :: forall fof atom term v f m. (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution3 fm =
+    askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution3 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+{-
+-- ------------------------------------------------------------------------- 
+-- The Pelletier examples again.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time presolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time presolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time presolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time presolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time presolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time presolution
+ <<p \/ ~p>>;;
+
+let p7 = time presolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time presolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time presolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time presolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time presolution
+ <<p <=> p>>;;
+
+let p12 = time presolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time presolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time presolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time presolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time presolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time presolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time presolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time presolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time presolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+let p21 = time presolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time presolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time presolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time presolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time presolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time presolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time presolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time presolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time presolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time presolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\
+   (forall x. (G(x) ==> ~U(x)) ==> P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time presolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time presolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time presolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time presolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time presolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time presolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time presolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+{- ** This one seems too slow
+
+let p38 = time presolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ** -}
+
+let p39 = time presolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time presolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time presolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+{- ** Also very slow
+
+let p42 = time presolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ** -}
+
+{- ** and this one too..
+
+let p43 = time presolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ** -}
+
+let p44 = time presolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+{- ** and this...
+
+let p45 = time presolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ** -}
+
+{- ** and this
+
+let p46 = time presolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time presolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time presolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time presolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time presolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time presolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+let gilmore_1 = time presolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time presolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time presolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time presolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time presolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time presolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This one still isn't easy!
+
+let gilmore_9 = time presolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time presolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+*********** -}
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Example                                                                   
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let gilmore_1 = resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletiers yet again.                                                     
+-- ------------------------------------------------------------------------- 
+
+{- ************
+
+let p1 = time resolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time resolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time resolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time resolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time resolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time resolution
+ <<p \/ ~p>>;;
+
+let p7 = time resolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time resolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time resolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time resolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time resolution
+ <<p <=> p>>;;
+
+let p12 = time resolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time resolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time resolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time resolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time resolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time resolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* Monadic Predicate Logic.                                                  *)
+(* ------------------------------------------------------------------------- *)
+
+let p18 = time resolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time resolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time resolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time resolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P) ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time resolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time resolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time resolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time resolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time resolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time resolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time resolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time resolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time resolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time resolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time resolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time resolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time resolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+   ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+  ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time resolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(*  Full predicate logic (without Identity and Functions)                    *)
+(* ------------------------------------------------------------------------- *)
+
+let p36 = time resolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time resolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+(*** This one seems too slow
+
+let p38 = time resolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ***)
+
+let p39 = time resolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time resolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time resolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+(*** Also very slow
+
+let p42 = time resolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ***)
+
+(*** and this one too..
+
+let p43 = time resolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ***)
+
+let p44 = time resolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+(*** and this...
+
+let p45 = time resolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ***)
+
+(*** and this
+
+let p46 = time resolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Manthey and Bry, CADE-9.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let p55 = time resolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time resolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* See info-hol, circa 1500.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let p58 = time resolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time resolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time resolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* From Gilmore's classic paper.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let gilmore_1 = time resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+(*** This is not valid, according to Gilmore
+
+let gilmore_2 = time resolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ***)
+
+let gilmore_3 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time resolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time resolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time resolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time resolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+(*** This one still isn't easy!
+
+let gilmore_9 = time resolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Davis-Putnam papers where Gilmore procedure is poor.         *)
+(* ------------------------------------------------------------------------- *)
+
+let davis_putnam_example = time resolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* The (in)famous Los problem.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let los = time resolution
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+************* -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Skolem.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Skolem.hs
@@ -0,0 +1,355 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Skolem
+    ( simplify
+    -- , simplify'
+    , lsimplify
+    , nnf
+    -- , nnf'
+    , pnf
+    -- , pnf'
+    , functions
+    -- , functions'
+    , SkolemT
+    , Skolem
+    , runSkolem
+    , runSkolemT
+    , specialize
+    , skolemize
+    -- , literal
+    , askolemize
+    , skolemNormalForm
+    -- , prenex'
+    , skolem
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (StateT(runStateT))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(exists, for_all, foldFirstOrder), quant, Quant(..), toPropositional)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import qualified Data.Logic.Classes.Skolem as C (Skolem(toSkolem))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Classes.Variable (Variable(variant))
+import Data.Logic.Harrison.FOL (fv, subst)
+import Data.Logic.Harrison.Lib ((|=>))
+import qualified Data.Map as Map (singleton)
+import qualified Data.Set as Set (empty, member, Set, toAscList, union)
+
+-- =========================================================================
+-- Prenex and Skolem normal forms.                                           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification. Like "psimplify" but with quantifier clauses.     
+-- ------------------------------------------------------------------------- 
+
+simplify1 :: (FirstOrderFormula fof atom v,
+              -- Formula fof term v,
+              Atom atom term v,
+              Term term v f) => fof -> fof
+simplify1 fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = if Set.member x (fv p) then fm else p
+      co ((:~:) p) = foldFirstOrder (\ _ _ _ -> fm) nco (fromBool . not) (\ _ -> fm) p
+      co (BinOp l op r) = simplifyBinop l op r
+      nco ((:~:) p) = p
+      nco _ = fm
+      tf = fromBool
+      at _ = fm
+
+simplifyBinop :: forall p. (Constants p, Combinable p) => p -> BinOp -> p -> p
+simplifyBinop l op r =
+    case (asBool l, op, asBool r) of
+      (Just True,  (:&:), _         ) -> r
+      (Just False, (:&:), _         ) -> false
+      (_,          (:&:), Just True ) -> l
+      (_,          (:&:), Just False) -> false
+      (Just True,  (:|:), _         ) -> true
+      (Just False, (:|:), _         ) -> r
+      (_,          (:|:), Just True ) -> true
+      (_,          (:|:), Just False) -> l
+      (Just True,  (:=>:), _         ) -> r
+      (Just False, (:=>:), _         ) -> true
+      (_,          (:=>:), Just True ) -> true
+      (_,          (:=>:), Just False) -> (.~.) l
+      (Just False, (:<=>:), Just False) -> true
+      (Just True,  (:<=>:), _         ) -> r
+      (Just False, (:<=>:), _         ) -> (.~.) r
+      (_,          (:<=>:), Just True ) -> l
+      (_,          (:<=>:), Just False) -> (.~.) l
+      _ -> binop l op r
+
+simplify :: (FirstOrderFormula fof atom v,
+             Atom atom term v,
+             Term term v f) => fof -> fof
+simplify fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x fm' = simplify1 (quant op x (simplify fm'))
+      co ((:~:) fm') = simplify1 ((.~.) (simplify fm'))
+      co (BinOp fm1 op fm2) = simplify1 (binop (simplify fm1) op (simplify fm2))
+      tf = fromBool
+      at _ = fm
+
+-- | Just looks for double negatives and negated constants.
+lsimplify :: Literal lit atom => lit -> lit
+lsimplify fm = foldLiteral (lsimplify1 . (.~.) . lsimplify) fromBool (const fm) fm
+
+lsimplify1 :: Literal lit atom => lit -> lit
+lsimplify1 fm = foldLiteral (foldLiteral id (fromBool . not) (const fm)) fromBool (const fm) fm
+
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf :: FirstOrderFormula formula atom v => formula -> formula
+nnf fm =
+    foldFirstOrder nnfQuant nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfQuant op v p = quant op v (nnf p)
+      nnfCombine ((:~:) p) = foldFirstOrder nnfNotQuant nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf p .&. nnf q
+      nnfCombine (BinOp p (:|:) q) = nnf p .|. nnf q
+      nnfNotQuant Forall v p = exists v (nnf ((.~.) p))
+      nnfNotQuant Exists v p = for_all v (nnf ((.~.) p))
+      nnfNotCombine ((:~:) p) = nnf p
+      nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- ------------------------------------------------------------------------- 
+-- Prenex normal form.                                                       
+-- ------------------------------------------------------------------------- 
+
+pullQuants :: forall formula atom v term f. (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+              formula -> formula
+pullQuants fm =
+    foldFirstOrder (\ _ _ _ -> fm) pullQuantsCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      getQuant = foldFirstOrder (\ op v f -> Just (op, v, f)) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing)
+      pullQuantsCombine ((:~:) _) = fm
+      pullQuantsCombine (BinOp l op r) = 
+          case (getQuant l, op, getQuant r) of
+            (Just (Forall, vl, l'), (:&:), Just (Forall, vr, r')) -> pullq True  True  fm for_all (.&.) vl vr l' r'
+            (Just (Exists, vl, l'), (:|:), Just (Exists, vr, r')) -> pullq True  True  fm exists  (.|.) vl vr l' r'
+            (Just (Forall, vl, l'), (:&:), _)                     -> pullq True  False fm for_all (.&.) vl vl l' r
+            (_,                     (:&:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.&.) vr vr l  r'
+            (Just (Forall, vl, l'), (:|:), _)                     -> pullq True  False fm for_all (.|.) vl vl l' r
+            (_,                     (:|:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.|.) vr vr l  r'
+            (Just (Exists, vl, l'), (:&:), _)                     -> pullq True  False fm exists  (.&.) vl vl l' r
+            (_,                     (:&:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.&.) vr vr l  r'
+            (Just (Exists, vl, l'), (:|:), _)                     -> pullq True  False fm exists  (.|.) vl vl l' r
+            (_,                     (:|:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.|.) vr vr l  r'
+            _                                                     -> fm
+
+-- |Helper function to rename variables when we want to enclose a
+-- formula containing a free occurrence of that variable a quantifier
+-- that quantifies it.
+pullq :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) =>
+         Bool -> Bool
+      -> formula
+      -> (v -> formula -> formula)
+      -> (formula -> formula -> formula)
+      -> v -> v
+      -> formula -> formula
+      -> formula
+pullq l r fm mkq op x y p q =
+    let z = variant x (fv fm)
+        p' = if l then subst (x |=> vt z) p else p
+        q' = if r then subst (y |=> vt z) q else q
+        fm' = pullQuants (op p' q') in
+    mkq z fm'
+
+-- |Recursivly apply pullQuants anywhere a quantifier might not be
+-- leftmost.
+prenex :: (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+          formula -> formula 
+prenex fm =
+    foldFirstOrder qu co (\ _ -> fm) (\ _ -> fm) fm
+    where
+      qu op x p = quant op x (prenex p)
+      co (BinOp l (:&:) r) = pullQuants (prenex l .&. prenex r)
+      co (BinOp l (:|:) r) = pullQuants (prenex l .|. prenex r)
+      co _ = fm
+
+-- |Convert to Prenex normal form, with all quantifiers at the left.
+pnf :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) => formula -> formula
+pnf = prenex . nnf . simplify
+
+-- ------------------------------------------------------------------------- 
+-- Get the functions in a term and formula.                                  
+-- ------------------------------------------------------------------------- 
+
+-- FIXME: the function parameter should be a method in the Atom class,
+-- but we need to add a type parameter f to it first.
+functions :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- State monad for generating Skolem functions and constants.
+-- ------------------------------------------------------------------------- 
+
+-- | Harrison's code generated skolem functions by adding a prefix to
+-- the variable name they are based on.  Here we have a more general
+-- and type safe solution: we require that variables be instances of
+-- class Skolem which creates Skolem functions based on an integer.
+-- This state value exists in the SkolemT monad during skolemization
+-- and tracks the next available number and the current list of
+-- universally quantified variables.
+
+data SkolemState v term
+    = SkolemState
+      { skolemCount :: Int
+        -- ^ The next available Skolem number.
+      , univQuant :: [v]
+        -- ^ The variables which are universally quantified in the
+        -- current scope, in the order they were encountered.  During
+        -- Skolemization these are the parameters passed to the Skolem
+        -- function.
+      }
+
+-- | The state associated with the Skolem monad.
+newSkolemState :: SkolemState v term
+newSkolemState
+    = SkolemState
+      { skolemCount = 1
+      , univQuant = []
+      }
+
+-- | The Skolem monad transformer
+type SkolemT v term m = StateT (SkolemState v term) m
+
+-- | Run a computation in the Skolem monad.
+runSkolem :: SkolemT v term Identity a -> a
+runSkolem = runIdentity . runSkolemT
+
+-- | The Skolem monad
+type Skolem v term = StateT (SkolemState v term) Identity
+
+-- | Run a computation in a stacked invocation of the Skolem monad.
+runSkolemT :: Monad m => SkolemT v term m a -> m a
+runSkolemT action = (runStateT action) newSkolemState >>= return . fst
+
+-- ------------------------------------------------------------------------- 
+-- Core Skolemization function.                                              
+-- ------------------------------------------------------------------------- 
+
+-- |Skolemize the formula by removing the existential quantifiers and
+-- replacing the variables they quantify with skolem functions (and
+-- constants, which are functions of zero variables.)  The Skolem
+-- functions are new functions (obtained from the SkolemT monad) which
+-- are applied to the list of variables which are universally
+-- quantified in the context where the existential quantifier
+-- appeared.
+skolem :: (Monad m,
+           FirstOrderFormula fof atom v,
+           -- PropositionalFormula pf atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          fof -> SkolemT v term m fof
+skolem fm =
+    foldFirstOrder qu co (return . fromBool) (return . atomic) fm
+    where
+      -- We encountered an existentially quantified variable y,
+      -- allocate a new skolem function fx and do a substitution to
+      -- replace occurrences of y with fx.  The value of the Skolem
+      -- function is assumed to equal the value of y which satisfies
+      -- the formula.
+      qu Exists y p =
+          do let xs = fv fm
+             let fx = fApp (C.toSkolem y) (map vt (Set.toAscList xs))
+             skolem (subst (Map.singleton y fx) p)
+      qu Forall x p = skolem p >>= return . for_all x
+      co (BinOp l (:&:) r) = skolem2 (.&.) l r
+      co (BinOp l (:|:) r) = skolem2 (.|.) l r
+      co _ = return fm
+
+skolem2 :: (Monad m,
+            FirstOrderFormula fof atom v,
+            -- PropositionalFormula pf atom,
+            -- Formula formula term v,
+            Atom atom term v,
+            Term term v f) =>
+           (fof -> fof -> fof) -> fof -> fof -> SkolemT v term m fof
+skolem2 cons p q =
+    skolem p >>= \ p' ->
+    skolem q >>= \ q' ->
+    return (cons p' q')
+
+-- ------------------------------------------------------------------------- 
+-- Overall Skolemization function.                                           
+-- ------------------------------------------------------------------------- 
+
+-- |I need to consult the Harrison book for the reasons why we don't
+-- |just Skolemize the result of prenexNormalForm.
+askolemize :: forall m fof atom term v f.
+              (Monad m,
+               FirstOrderFormula fof atom v,
+               Atom atom term v,
+               Term term v f) =>
+              fof -> SkolemT v term m fof
+askolemize = skolem . nnf . simplify
+
+-- | Remove the leading universal quantifiers.  After a call to pnf
+-- this will be all the universal quantifiers, and the skolemization
+-- will have already turned all the existential quantifiers into
+-- skolem functions.
+specialize :: forall fof atom v. FirstOrderFormula fof atom v => fof -> fof
+specialize f =
+    foldFirstOrder q (\ _ -> f) (\ _ -> f) (\ _ -> f) f
+    where
+      q Forall _ f' = specialize f'
+      q _ _ _ = f
+
+-- | Skolemize and then specialize.  Because we know all quantifiers
+-- are gone we can convert to any instance of PropositionalFormula.
+skolemize :: forall m fof atom term v f pf atom2. (Monad m,
+              FirstOrderFormula fof atom v,
+              PropositionalFormula pf atom2,
+              Atom atom term v,
+              Term term v f,
+              Eq pf) =>
+             (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemize ca fm = askolemize fm >>= return . (toPropositional ca :: fof -> pf) . specialize . pnf
+
+{-
+-- | Convert a first order formula into a disjunct of conjuncts of
+-- literals.  Note that this can convert any instance of
+-- FirstOrderFormula into any instance of Literal.
+literal :: forall fof atom term v p f lit. (Literal fof atom, Apply atom p term, Term term v f, Literal lit atom, Formula lit atom, Ord lit) =>
+           fof -> Set.Set (Set.Set lit)
+literal fm =
+    foldLiteral neg tf at fm
+    where
+      neg :: fof -> Set.Set (Set.Set lit)
+      neg x = Set.map (Set.map (.~.)) (literal x)
+      tf = Set.singleton . Set.singleton . fromBool
+      at :: atom -> Set.Set (Set.Set lit)
+      at x = foldApply (\ _ _ -> Set.singleton (Set.singleton (atomic x))) tf x
+-}
+
+-- |We get Skolem Normal Form by skolemizing and then converting to
+-- Prenex Normal Form, and finally eliminating the remaining quantifiers.
+skolemNormalForm :: (FirstOrderFormula fof atom v,
+                     PropositionalFormula pf atom2,
+                     -- Formula fof term v,
+                     -- Formula pf term v,
+                     Atom atom term v,
+                     Term term v f,
+                     Monad m, Ord fof, Eq pf) =>
+                    (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemNormalForm = skolemize
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Tableaux.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Tableaux.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Tableaux.hs
@@ -0,0 +1,624 @@
+{-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Tableaux
+    ( unify_literals
+    , unifyAtomsEq
+    , deepen
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(freeVariables, substitute, unify))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Literal.ZipLiterals (zipLiterals)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(..), vt)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib', settryfind)
+import Data.Logic.Harrison.Prop (simpdnf)
+import Data.Logic.Harrison.Skolem (runSkolem, skolemize)
+import Data.Logic.Harrison.Unif (unify)
+import qualified Data.Map as Map (empty, fromList, Map)
+import qualified Data.Set as Set (empty, map, minView, partition, Set, singleton, toList, union)
+import Data.String (IsString(..))
+import Debug.Trace (trace)
+
+-- =========================================================================
+-- Tableaux, seen as an optimized version of a Prawitz-like procedure.       
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Unify literals (just pretend the toplevel relation is a function).        
+-- ------------------------------------------------------------------------- 
+
+unify_literals :: forall lit atom term v f.
+                  (Literal lit atom,
+                   C.Atom atom term v,
+                   Term term v f) =>
+                  Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_literals env f1 f2 =
+    maybe err id (zipLiterals co tf at f1 f2)
+    where
+      -- co :: lit -> lit -> Maybe (Failing (Map.Map v term))
+      co p q = Just $ unify_literals env p q
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      at :: atom -> atom -> Maybe (Failing (Map.Map v term))
+      at a1 a2 = Just $ C.unify env a1 a2
+      err = Failure ["Can't unify literals"]
+
+unifyAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+unifyAtomsEq env a1 a2 =
+    maybe err id (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p1 ts1 p2 ts2 =
+          if p1 == p2 && length ts1 == length ts2
+          then Just $ unify env (zip ts1 ts2)
+          else Nothing
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      eq pl pr ql qr = Just $ unify env [(pl, ql), (pr, qr)]
+      err = Failure ["Can't unify atoms"]
+
+-- ------------------------------------------------------------------------- 
+-- Unify complementary literals.                                             
+-- ------------------------------------------------------------------------- 
+
+unify_complements :: forall lit atom term v f.
+                     (Literal lit atom,
+                      C.Atom atom term v,
+                      Term term v f) =>
+                     Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_complements env p q = unify_literals env p ((.~.) q)
+
+-- ------------------------------------------------------------------------- 
+-- Unify and refute a set of disjuncts.                                      
+-- ------------------------------------------------------------------------- 
+
+unify_refute :: (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) => Set.Set (Set.Set lit) -> Map.Map v term -> Failing (Map.Map v term)
+unify_refute djs env =
+    case Set.minView djs of
+      Nothing -> Success env
+      Just (d, odjs) ->
+          settryfind (\ (p, n) -> unify_complements env p n >>= unify_refute odjs) pairs
+          where
+            pairs = allpairs (,) pos neg
+            (pos,neg) = Set.partition positive d
+
+-- ------------------------------------------------------------------------- 
+-- Hence a Prawitz-like procedure (using unification on DNF).                
+-- ------------------------------------------------------------------------- 
+
+prawitz_loop :: forall atom v term f lit. (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit) -> [v] -> Set.Set (Set.Set lit) -> Int -> (Map.Map v term, Int)
+prawitz_loop djs0 fvs djs n =
+    let l = length fvs in
+    let newvars = map (\ k -> fromString ("_" ++ show (n * l + k))) [1..l] in
+    let inst = Map.fromList (zip fvs (map vt newvars)) in
+    let djs1 = distrib' (Set.map (Set.map (mapAtoms (atomic . substitute' inst))) djs0) djs in
+    case unify_refute djs1 Map.empty of
+      Failure _ -> prawitz_loop djs0 fvs djs1 (n + 1)
+      Success env -> (env, n + 1)
+    where
+      substitute' :: Map.Map v term -> atom -> atom
+      substitute' = C.substitute
+
+-- prawitz :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Int
+prawitz :: forall fof atom term v f lit pf.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal lit atom,
+            Term term v f,
+            C.Atom atom term v) =>
+           fof -> Int
+prawitz fm =
+    snd (prawitz_loop dnf (Set.toList fvs) dnf0 0 :: (Map.Map v term, Int))
+    where
+      dnf0 = (Set.singleton Set.empty) :: Set.Set (Set.Set lit)
+      dnf = simpdnf pf :: Set.Set (Set.Set lit)
+      fvs = foldAtoms (\ s (a :: atom) -> Set.union (C.freeVariables a) s) Set.empty pf :: Set.Set v
+      pf = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+test01 = TestCase $ assertEqual "p20 - prawitz" expected input
+    where input = prawitz fm
+          fm = (for_all "x" (for_all "y" (exists "z" (for_all "w" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"] .=>.
+                                                                   pApp "R" [vt "z"] .&. pApp "U" [vt "w"]))))) .=>.
+               (exists "x" (exists "y" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"]))) .=>. (exists "z" (pApp "R" [vt "z"]))
+          expected = 1
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Comparison of number of ground instances.                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+compare :: forall fof pf lit atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            C.Atom atom term v,
+            IsString f) =>
+           (atom -> Set.Set (f, Int)) -> fof -> (Int, Failing Int)
+-}
+-- compare fa fm = (prawitz fm, davisputnam fa fm)
+{-
+START_INTERACTIVE;;
+test02 = TestCase $ assertEqual "p19" expected input
+    where input = compare (exists "x" (forall "y" (for_all "z" ((pApp "P" [vt "y"] .=>. pApp "Q" [vt "z"]) .=>. pApp "P" [vt "x"] .=>. pApp "Q" [vt "x"]))))
+
+let p20 = compare
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p24 = compare
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"])
+   .=>. (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p39 = compare
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p42 = compare
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+{- **** Too slow?
+
+let p43 = compare
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+ ***** -}
+
+let p44 = compare
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y)))
+   .=>. (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p59 = compare
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = compare
+ <<forall x. P(x,f[vt "x"]) .<=>.
+             exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- More standard tableau procedure, effectively doing DNF incrementally.     
+-- ------------------------------------------------------------------------- 
+
+let rec tableau (fms,lits,n) cont (env,k) =
+  if n < 0 then error "no proof at this level" else
+  match fms with
+    [] -> error "tableau: no proof"
+  | And(p,q) : unexp ->
+      tableau (p : q : unexp,lits,n) cont (env,k)
+  | Or(p,q) : unexp ->
+      tableau (p : unexp,lits,n) (tableau (q : unexp,lits,n) cont) (env,k)
+  | Forall(x,p) : unexp ->
+      let y = Vt("_" ++ string_of_int k) in
+      let p' = subst (x |=> y) p in
+      tableau (p' : unexp@[Forall(x,p)],lits,n-1) cont (env,k+1)
+  | fm : unexp ->
+      try tryfind (\ l -> cont(unify_complements env (fm,l),k)) lits
+      with Failure _ -> tableau (unexp,fm : lits,n) cont (env,k);;
+-}
+
+-- | Try f with higher and higher values of n until it succeeds, or
+-- optional maximum depth limit is exceeded.
+deepen :: (Int -> Failing t) -> Int -> Maybe Int -> Failing (t, Int)
+deepen _ n (Just m) | n > m = Failure ["Exceeded maximum depth limit"]
+deepen f n m =
+    -- If no maximum depth limit is given print a trace of the
+    -- levels tried.  The assumption is that we are running
+    -- interactively.
+    let n' = maybe (trace ("Searching with depth limit " ++ show n) n) (const n) m in
+    case f n' of
+      Failure _ -> deepen f (n + 1) m
+      Success x -> Success (x, n)
+
+{-
+let tabrefute fms =
+  deepen (\ n -> tableau (fms,[],n) (\ x -> x) (Map.empty,0); n) 0;;
+
+let tab fm =
+  let sfm = askolemize(Not(generalize fm)) in
+  if sfm = False then 0 else tabrefute [sfm];;
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p38 = tab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Try to split up the initial formula first; often a big improvement.       
+-- ------------------------------------------------------------------------- 
+
+let splittab fm = 
+  map tabrefute (simpdnf(askolemize(Not(generalize fm))));;
+
+-- ------------------------------------------------------------------------- 
+-- Example: the Andrews challenge.                                           
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p34 = splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Another nice example from EWD 1602.                                       
+-- ------------------------------------------------------------------------- 
+
+let ewd1062 = splittab
+ <<(forall x. x <= x) .&.
+   (forall x y z. x <= y .&. y <= z .=>. x <= z) .&.
+   (forall x y. f[vt "x"] <= y .<=>. x <= g[vt "y"])
+   .=>. (forall x y. x <= y .=>. f[vt "x"] <= f[vt "y"]) .&.
+       (forall x y. x <= y .=>. g[vt "x"] <= g[vt "y"])>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Do all the equality-free Pelletier problems, and more, as examples.       
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time splittab
+ <<p .=>. q .<=>. ~q .=>. ~p>>;;
+
+let p2 = time splittab
+ <<~ ~p .<=>. p>>;;
+
+let p3 = time splittab
+ <<~(p .=>. q) .=>. q .=>. p>>;;
+
+let p4 = time splittab
+ <<~p .=>. q .<=>. ~q .=>. p>>;;
+
+let p5 = time splittab
+ <<(p .|. q .=>. p .|. r) .=>. p .|. (q .=>. r)>>;;
+
+let p6 = time splittab
+ <<p .|. ~p>>;;
+
+let p7 = time splittab
+ <<p .|. ~ ~ ~p>>;;
+
+let p8 = time splittab
+ <<((p .=>. q) .=>. p) .=>. p>>;;
+
+let p9 = time splittab
+ <<(p .|. q) .&. (~p .|. q) .&. (p .|. ~q) .=>. ~(~q .|. ~q)>>;;
+
+let p10 = time splittab
+ <<(q .=>. r) .&. (r .=>. p .&. q) .&. (p .=>. q .&. r) .=>. (p .<=>. q)>>;;
+
+let p11 = time splittab
+ <<p .<=>. p>>;;
+
+let p12 = time splittab
+ <<((p .<=>. q) .<=>. r) .<=>. (p .<=>. (q .<=>. r))>>;;
+
+let p13 = time splittab
+ <<p .|. q .&. r .<=>. (p .|. q) .&. (p .|. r)>>;;
+
+let p14 = time splittab
+ <<(p .<=>. q) .<=>. (q .|. ~p) .&. (~q .|. p)>>;;
+
+let p15 = time splittab
+ <<p .=>. q .<=>. ~p .|. q>>;;
+
+let p16 = time splittab
+ <<(p .=>. q) .|. (q .=>. p)>>;;
+
+let p17 = time splittab
+ <<p .&. (q .=>. r) .=>. s .<=>. (~p .|. q .|. s) .&. (~p .|. ~r .|. s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletier problems: monadic predicate logic.                              
+-- ------------------------------------------------------------------------- 
+
+let p18 = time splittab
+ <<exists y. forall x. P[vt "y"] .=>. P[vt "x"]>>;;
+
+let p19 = time splittab
+ <<exists x. forall y z. (P[vt "y"] .=>. Q[vt "z"]) .=>. P[vt "x"] .=>. Q[vt "x"]>>;;
+
+let p20 = time splittab
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p21 = time splittab
+ <<(exists x. P .=>. Q[vt "x"]) .&. (exists x. Q[vt "x"] .=>. P)
+   .=>. (exists x. P .<=>. Q[vt "x"])>>;;
+
+let p22 = time splittab
+ <<(forall x. P .<=>. Q[vt "x"]) .=>. (P .<=>. (forall x. Q[vt "x"]))>>;;
+
+let p23 = time splittab
+ <<(forall x. P .|. Q[vt "x"]) .<=>. P .|. (forall x. Q[vt "x"])>>;;
+
+let p24 = time splittab
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"]) .=>.
+   (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p25 = time splittab
+ <<(exists x. P[vt "x"]) .&.
+   (forall x. U[vt "x"] .=>. ~G[vt "x"] .&. R[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. G[vt "x"] .&. U[vt "x"]) .&.
+   ((forall x. P[vt "x"] .=>. Q[vt "x"]) .|. (exists x. Q[vt "x"] .&. P[vt "x"]))
+   .=>. (exists x. Q[vt "x"] .&. P[vt "x"])>>;;
+
+let p26 = time splittab
+ <<((exists x. P[vt "x"]) .<=>. (exists x. Q[vt "x"])) .&.
+   (forall x y. P[vt "x"] .&. Q[vt "y"] .=>. (R[vt "x"] .<=>. U[vt "y"]))
+   .=>. ((forall x. P[vt "x"] .=>. R[vt "x"]) .<=>. (forall x. Q[vt "x"] .=>. U[vt "x"]))>>;;
+
+let p27 = time splittab
+ <<(exists x. P[vt "x"] .&. ~Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. R[vt "x"]) .&.
+   (forall x. U[vt "x"] .&. V[vt "x"] .=>. P[vt "x"]) .&.
+   (exists x. R[vt "x"] .&. ~Q[vt "x"])
+   .=>. (forall x. U[vt "x"] .=>. ~R[vt "x"])
+       .=>. (forall x. U[vt "x"] .=>. ~V[vt "x"])>>;;
+
+let p28 = time splittab
+ <<(forall x. P[vt "x"] .=>. (forall x. Q[vt "x"])) .&.
+   ((forall x. Q[vt "x"] .|. R[vt "x"]) .=>. (exists x. Q[vt "x"] .&. R[vt "x"])) .&.
+   ((exists x. R[vt "x"]) .=>. (forall x. L[vt "x"] .=>. M[vt "x"])) .=>.
+   (forall x. P[vt "x"] .&. L[vt "x"] .=>. M[vt "x"])>>;;
+
+let p29 = time splittab
+ <<(exists x. P[vt "x"]) .&. (exists x. G[vt "x"]) .=>.
+   ((forall x. P[vt "x"] .=>. H[vt "x"]) .&. (forall x. G[vt "x"] .=>. J[vt "x"]) .<=>.
+    (forall x y. P[vt "x"] .&. G[vt "y"] .=>. H[vt "x"] .&. J[vt "y"]))>>;;
+
+let p30 = time splittab
+ <<(forall x. P[vt "x"] .|. G[vt "x"] .=>. ~H[vt "x"]) .&.
+   (forall x. (G[vt "x"] .=>. ~U[vt "x"]) .=>. P[vt "x"] .&. H[vt "x"])
+   .=>. (forall x. U[vt "x"])>>;;
+
+let p31 = time splittab
+ <<~(exists x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"])) .&.
+   (exists x. Q[vt "x"] .&. P[vt "x"]) .&.
+   (forall x. ~H[vt "x"] .=>. J[vt "x"])
+   .=>. (exists x. Q[vt "x"] .&. J[vt "x"])>>;;
+
+let p32 = time splittab
+ <<(forall x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"]) .=>. Q[vt "x"]) .&.
+   (forall x. Q[vt "x"] .&. H[vt "x"] .=>. J[vt "x"]) .&.
+   (forall x. R[vt "x"] .=>. H[vt "x"])
+   .=>. (forall x. P[vt "x"] .&. R[vt "x"] .=>. J[vt "x"])>>;;
+
+let p33 = time splittab
+ <<(forall x. P[vt "a"] .&. (P[vt "x"] .=>. P[vt "b"]) .=>. P[vt "c"]) .<=>.
+   (forall x. P[vt "a"] .=>. P[vt "x"] .|. P[vt "c"]) .&. (P[vt "a"] .=>. P[vt "b"] .=>. P[vt "c"])>>;;
+
+let p34 = time splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+let p35 = time splittab
+ <<exists x y. P(x,y) .=>. (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Full predicate logic (without identity and functions).                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time splittab
+ <<(forall x. exists y. P(x,y)) .&.
+   (forall x. exists y. G(x,y)) .&.
+   (forall x y. P(x,y) .|. G(x,y)
+   .=>. (forall z. P(y,z) .|. G(y,z) .=>. H(x,z)))
+       .=>. (forall x. exists y. H(x,y))>>;;
+
+let p37 = time splittab
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) .=>. P(y,w)) .&. P(y,z) .&.
+     (P(y,w) .=>. (exists u. Q(u,w)))) .&.
+   (forall x z. ~P(x,z) .=>. (exists y. Q(y,z))) .&.
+   ((exists x y. Q(x,y)) .=>. (forall x. R(x,x))) .=>.
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time splittab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+
+let p39 = time splittab
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p40 = time splittab
+ <<(exists y. forall x. P(x,y) .<=>. P(x,x))
+  .=>. ~(forall x. exists y. forall z. P(z,y) .<=>. ~P(z,x))>>;;
+
+let p41 = time splittab
+ <<(forall z. exists y. forall x. P(x,y) .<=>. P(x,z) .&. ~P(x,x))
+  .=>. ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time splittab
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+let p43 = time splittab
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+let p44 = time splittab
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y))) .=>.
+   (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p45 = time splittab
+ <<(forall x.
+     P[vt "x"] .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y)) .=>.
+       (forall y. G[vt "y"] .&. H(x,y) .=>. R[vt "y"])) .&.
+   ~(exists y. L[vt "y"] .&. R[vt "y"]) .&.
+   (exists x. P[vt "x"] .&. (forall y. H(x,y) .=>.
+     L[vt "y"]) .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y))) .=>.
+   (exists x. P[vt "x"] .&. ~(exists y. G[vt "y"] .&. H(x,y)))>>;;
+
+let p46 = time splittab
+ <<(forall x. P[vt "x"] .&. (forall y. P[vt "y"] .&. H(y,x) .=>. G[vt "y"]) .=>. G[vt "x"]) .&.
+   ((exists x. P[vt "x"] .&. ~G[vt "x"]) .=>.
+    (exists x. P[vt "x"] .&. ~G[vt "x"] .&.
+               (forall y. P[vt "y"] .&. ~G[vt "y"] .=>. J(x,y)))) .&.
+   (forall x y. P[vt "x"] .&. P[vt "y"] .&. H(x,y) .=>. ~J(y,x)) .=>.
+   (forall x. P[vt "x"] .=>. G[vt "x"])>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Well-known "Agatha" example; cf. Manthey and Bry, CADE-9.                 
+-- ------------------------------------------------------------------------- 
+
+let p55 = time splittab
+ <<lives(agatha) .&. lives(butler) .&. lives(charles) .&.
+   (killed(agatha,agatha) .|. killed(butler,agatha) .|.
+    killed(charles,agatha)) .&.
+   (forall x y. killed(x,y) .=>. hates(x,y) .&. ~richer(x,y)) .&.
+   (forall x. hates(agatha,x) .=>. ~hates(charles,x)) .&.
+   (hates(agatha,agatha) .&. hates(agatha,charles)) .&.
+   (forall x. lives[vt "x"] .&. ~richer(x,agatha) .=>. hates(butler,x)) .&.
+   (forall x. hates(agatha,x) .=>. hates(butler,x)) .&.
+   (forall x. ~hates(x,agatha) .|. ~hates(x,butler) .|. ~hates(x,charles))
+   .=>. killed(agatha,agatha) .&.
+       ~killed(butler,agatha) .&.
+       ~killed(charles,agatha)>>;;
+
+let p57 = time splittab
+ <<P(f([vt "a"],b),f(b,c)) .&.
+   P(f(b,c),f(a,c)) .&.
+   (forall [vt "x"] y z. P(x,y) .&. P(y,z) .=>. P(x,z))
+   .=>. P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time splittab
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P[vt "x"] .&. Q[vt "y"]) .=>. ((P[vt "v"] .|. R[vt "w"])  .&. (R[vt "z"] .=>. Q[vt "v"])))>>;;
+
+let p59 = time splittab
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = time splittab
+ <<forall x. P(x,f[vt "x"]) .<=>.
+            exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **** This is still too hard for us! Amazing...
+
+let gilmore_1 = time splittab
+ <<exists x. forall y z.
+      ((F[vt "y"] .=>. G[vt "y"]) .<=>. F[vt "x"]) .&.
+      ((F[vt "y"] .=>. H[vt "y"]) .<=>. G[vt "x"]) .&.
+      (((F[vt "y"] .=>. G[vt "y"]) .=>. H[vt "y"]) .<=>. H[vt "x"])
+      .=>. F[vt "z"] .&. G[vt "z"] .&. H[vt "z"]>>;;
+
+ ***** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time splittab
+ <<exists x y. forall z.
+        (F(x,z) .<=>. F(z,y)) .&. (F(z,y) .<=>. F(z,z)) .&. (F(x,y) .<=>. F(y,x))
+        .=>. (F(x,y) .<=>. F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. H[vt "x"])) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. H[vt "z"]) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_4 = time splittab
+ <<exists x y. forall z.
+        (F(x,y) .=>. F(y,z) .&. F(z,z)) .&.
+        (F(x,y) .&. G(x,y) .=>. G(x,z) .&. G(z,z))>>;;
+
+let gilmore_5 = time splittab
+ <<(forall x. exists y. F(x,y) .|. F(y,x)) .&.
+   (forall x y. F(y,x) .=>. F(y,y))
+   .=>. exists z. F(z,z)>>;;
+
+let gilmore_6 = time splittab
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) .=>. G(v,u) .&. G(u,x))
+        .=>. (exists u. forall v. F(u,y) .=>. G(v,u) .&. G(u,y)) .|.
+            (forall u v. exists w. G(v,u) .|. H(w,y,u) .=>. G(u,w))>>;;
+
+let gilmore_7 = time splittab
+ <<(forall x. K[vt "x"] .=>. exists y. L[vt "y"] .&. (F(x,y) .=>. G(x,y))) .&.
+   (exists z. K[vt "z"] .&. forall u. L[vt "u"] .=>. F(z,u))
+   .=>. exists v w. K[vt "v"] .&. L[vt "w"] .&. G(v,w)>>;;
+
+let gilmore_8 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. (forall u. exists v. H(u,v,x)))) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. (forall u. exists v. H(u,v,z))) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_9 = time splittab
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))) .&.
+        ((forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))
+         .=>. ~(forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+         .=>. (forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x)) .&.
+             (forall u. exists v. F(z,u,v) .&. G(y,u) .&. ~H(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time splittab
+ <<exists x. exists y. forall z.
+        (F(x,y) .=>. (F(y,z) .&. F(z,z))) .&.
+        ((F(x,y) .&. G(x,y)) .=>. (G(x,z) .&. G(z,z)))>>;;
+
+************ -}
+
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Harrison/Unif.hs b/testdata/split-merge-expected/Data/Logic/Harrison/Unif.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Harrison/Unif.hs
@@ -0,0 +1,125 @@
+{-# OPTIONS -Wall #-}
+module Data.Logic.Harrison.Unif
+    ( unify
+    , solve
+    , fullUnify
+    , unifyAndApply
+    ) where
+
+import Data.Logic.Classes.Term (Term(..), tsubst)
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+{-
+(* ========================================================================= *)
+(* Unification for first order terms.                                        *)
+(*                                                                           *)
+(* Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  *)
+(* ========================================================================= *)
+
+let rec istriv env x t =
+  match t with
+    Var y -> y = x or defined env y & istriv env x (apply env y)
+  | Fn(f,args) -> exists (istriv env x) args & failwith "cyclic";;
+-}
+isTrivial :: Term term v f => Map.Map v term -> v -> term -> Failing Bool
+isTrivial env x t =
+    foldTerm v f t
+    where
+      v y =
+          if x == y
+          then Success True
+          else maybe (Success False) (isTrivial env x) (Map.lookup y env)
+      f _ args =
+          if any (failing (const False) id . isTrivial env x) args
+          then Failure ["cyclic"]
+          else Success False
+
+{-
+    foldT (\ y -> y == x || (defined env y && istriv env x (apply env y)))
+          (\ _ args -> if any (istriv env x) args then error "cyclic" else False)
+          t
+-}
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Main unification procedure                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec unify env eqs =
+  match eqs with
+    [] -> env
+  | (Fn(f,fargs),Fn(g,gargs))::oth ->
+        if f = g & length fargs = length gargs
+        then unify env (zip fargs gargs @ oth)
+        else failwith "impossible unification"
+  | (Var x,t)::oth | (t,Var x)::oth ->
+        if defined env x then unify env ((apply env x,t)::oth)
+        else unify (if istriv env x t then env else (x|->t) env) oth;;
+-}
+unify :: Term term v f => Map.Map v term -> [(term,term)] -> Failing (Map.Map v term)
+unify env [] = Success env
+unify env ((a,b):oth) =
+    foldTerm (vr b) (\ f fargs -> foldTerm (vr a) (fn f fargs) b) a
+    where
+      vr t x =
+          maybe (isTrivial env x t >>= \ trivial -> unify (if trivial then env else Map.insert x t env) oth)
+                (\ y -> unify env ((y, t) : oth))
+                (Map.lookup x env)
+      fn f fargs g gargs =
+          if f == g && length fargs == length gargs
+          then unify env (zip fargs gargs ++ oth)
+          else Failure ["impossible unification"]
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Solve to obtain a single instantiation.                                   *)
+(* ------------------------------------------------------------------------- *)
+
+let rec solve env =
+  let env' = mapf (tsubst env) env in
+  if env' = env then env else solve env';;
+-}
+solve :: Term term v f => Map.Map v term -> Map.Map v term
+solve env =
+    if env' == env then env else solve env'
+    where env' = Map.map (tsubst env) env
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Unification reaching a final solved form (often this isn't needed).       *)
+(* ------------------------------------------------------------------------- *)
+
+let fullunify eqs = solve (unify undefined eqs);;
+-}
+fullUnify :: Term term v f => [(term,term)] -> Failing (Map.Map v term)
+fullUnify eqs = failing Failure (Success . solve) (unify Map.empty eqs)
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Examples.                                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let unify_and_apply eqs =
+  let i = fullunify eqs in
+  let apply (t1,t2) = tsubst i t1,tsubst i t2 in
+  map apply eqs;;
+-}
+unifyAndApply :: Term term v f => [(term, term)] -> Failing [(term, term)]
+unifyAndApply eqs =
+    case fullUnify eqs of
+      Failure x -> Failure x
+      Success i -> Success (map (\ (t1, t2) -> (tsubst i t1, tsubst i t2)) eqs)
+{-
+
+START_INTERACTIVE;;
+unify_and_apply [<<|f(x,g(y))|>>,<<|f(f(z),w)|>>];;
+
+unify_and_apply [<<|f(x,y)|>>,<<|f(y,x)|>>];;
+
+(****  unify_and_apply [<<|f(x,g(y))|>>,<<|f(y,x)|>>];; *****)
+
+unify_and_apply [<<|x_0|>>,<<|f(x_1,x_1)|>>;
+                 <<|x_1|>>,<<|f(x_2,x_2)|>>;
+                 <<|x_2|>>,<<|f(x_3,x_3)|>>];;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Instances/Chiou.hs b/testdata/split-merge-expected/Data/Logic/Instances/Chiou.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Instances/Chiou.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             RankNTypes, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall -Wwarn -fno-warn-orphans -fno-warn-missing-signatures #-}
+module Data.Logic.Instances.Chiou
+    ( Sentence(..)
+    , CTerm(..)
+    , Connective(..)
+    , Quantifier(..)
+    , ConjunctiveNormalForm(..)
+    , NormalSentence(..)
+    , NormalTerm(..)
+    , toSentence
+    , fromSentence
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..), false, true)
+import Data.Logic.Classes.Equals ((.=.), AtomEq(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, pApp, prettyFirstOrder, Quant(..), quant')
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Negate ((.~.), Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Function, Term(..))
+import Data.Logic.Classes.Variable (Variable)
+import Data.String (IsString(..))
+
+data Sentence v p f
+    = Connective (Sentence v p f) Connective (Sentence v p f)
+    | Quantifier Quantifier [v] (Sentence v p f)
+    | Not (Sentence v p f)
+    | Predicate p [CTerm v f]
+    | Equal (CTerm v f) (CTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+data CTerm v f
+    = Function f [CTerm v f]
+    | Variable v
+    deriving (Eq, Ord, Data, Typeable)
+
+data Connective
+    = Imply
+    | Equiv
+    | And
+    | Or
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+data Quantifier
+    = ForAll
+    | ExistsCh
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Negatable (Sentence v p f) where
+    negatePrivate = Not
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Constants p, Eq (Sentence v p f)) => Constants (Sentence v p f) where
+    fromBool x = Predicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance ({- Constants (Sentence v p f), -} Ord v, Ord p, Ord f) => Combinable (Sentence v p f) where
+    x .<=>. y = Connective x Equiv y
+    x .=>.  y = Connective x Imply y
+    x .|.   y = Connective x Or y
+    x .&.   y = Connective x And y
+
+instance (Predicate p, Function f v) => Formula (Sentence v p f) (Sentence v p f) where
+    atomic (Connective _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Quantifier _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Not _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic x@(Predicate _ _) = x
+    atomic x@(Equal _ _) = x
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f), Variable v, Predicate p, Function f v, Combinable (Sentence v p f)) =>
+         PropositionalFormula (Sentence v p f) (Sentence v p f) where
+    foldPropositional co tf at formula =
+        case formula of
+          Not x -> co ((:~:) x)
+          Quantifier _ _ _ -> error "Logic.Instance.Chiou.foldF0: unexpected"
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate p ts -> maybe (at (Predicate p ts)) tf (asBool p)
+          Equal t1 t2 -> at (Equal t1 t2)
+
+data AtomicFunction v
+    = AtomicFunction String
+    -- This is redundant with the SkolemFunction and SkolemConstant
+    -- constructors in the Chiou Term type.
+    | AtomicSkolemFunction v
+    deriving (Eq, Show)
+
+instance IsString (AtomicFunction v) where
+    fromString = AtomicFunction
+
+instance Variable v => Skolem (AtomicFunction v) v where
+    toSkolem = AtomicSkolemFunction
+    isSkolem (AtomicSkolemFunction _) = True
+    isSkolem _ = False
+
+-- The Atom type is not cleanly distinguished from the Sentence type, so we need an Atom instance for Sentence.
+instance (Variable v, Predicate p, Function f v) => Apply (Sentence v p f) p (CTerm v f) where
+    foldApply ap tf (Predicate p ts) = maybe (ap p ts) tf (asBool p)
+    foldApply _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    apply' = Predicate
+
+instance Predicate p => AtomEq (Sentence v p f) p (CTerm v f) where
+    foldAtomEq ap tf _ (Predicate p ts) = if p == true then tf True else if p == false then tf False else ap p ts
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    foldAtomEq _ _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    equals = Equal
+    applyEq' = Predicate
+
+instance (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Variable v, Predicate p, Function f v) => Pretty (Sentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Formula (Sentence v p f) (Sentence v p f), Predicate p, Function f v, Variable v) => HasFixity (Sentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f),
+          Variable v, Predicate p, Function f v) =>
+          FirstOrderFormula (Sentence v p f) (Sentence v p f) v where
+    for_all v x = Quantifier ForAll [v] x
+    exists v x = Quantifier ExistsCh [v] x
+    foldFirstOrder qu co tf at f =
+        case f of
+          Not x -> co ((:~:) x)
+          Quantifier op (v:vs) f' ->
+              let op' = case op of
+                          ForAll -> Forall
+                          ExistsCh -> Exists in
+              -- Use Logic.quant' here instead of the constructor
+              -- Quantifier so as not to create quantifications with
+              -- empty variable lists.
+              qu op' v (quant' op' vs f')
+          Quantifier _ [] f' -> foldFirstOrder qu co tf at f'
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate _ _ -> at f
+          Equal _ _ -> at f
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Not f1', Not f2') -> co ((:~:) f1') ((:~:) f2')
+          (Quantifier op1 (v1:vs1) f1', Quantifier op2 (v2:vs2) f2') ->
+              if op1 == op2
+              then let op' = case op1 of
+                               ForAll -> Forall
+                               ExistsCh -> Exists in
+                   qu op' v1 (Quantifier op1 vs1 f1') Forall v2 (Quantifier op2 vs2 f2')
+              else Nothing
+          (Quantifier q1 [] f1', Quantifier q2 [] f2') ->
+              if q1 == q2 then zipFirstOrder qu co tf at f1' f2' else Nothing
+          (Connective l1 op1 r1, Connective l2 op2 r2) ->
+              case (op1, op2) of
+                (And, And) -> co (BinOp l1 (:&:) r1) (BinOp l2 (:&:) r2)
+                (Or, Or) -> co (BinOp l1 (:|:) r1) (BinOp l2 (:|:) r2)
+                (Imply, Imply) -> co (BinOp l1 (:=>:) r1) (BinOp l2 (:=>:) r2)
+                (Equiv, Equiv) -> co (BinOp l1 (:<=>:) r1) (BinOp l2 (:<=>:) r2)
+                _ -> Nothing
+          (Equal _ _, Equal _ _) -> at f1 f2
+          (Predicate _ _, Predicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Variable v, Function f v) => Term (CTerm v f) v f where
+    foldTerm v fn t =
+        case t of
+          Variable x -> v x
+          Function f ts -> fn f ts
+    zipTerms  v f t1 t2 =
+        case (t1, t2) of
+          (Variable v1, Variable v2) -> v v1 v2
+          (Function f1 ts1, Function f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Variable
+    fApp f ts = Function f ts
+
+data ConjunctiveNormalForm v p f =
+    CNF [Sentence v p f]
+    deriving (Eq)
+
+data NormalSentence v p f
+    = NFNot (NormalSentence v p f)
+    | NFPredicate p [NormalTerm v f]
+    | NFEqual (NormalTerm v f) (NormalTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+-- We need a distinct type here because of the functional dependencies
+-- in class FirstOrderFormula.
+data NormalTerm v f
+    = NormalFunction f [NormalTerm v f]
+    | NormalVariable v
+    deriving (Eq, Ord, Data, Typeable)
+
+instance (Constants p, Eq (NormalSentence v p f)) => Constants (NormalSentence v p f) where
+    fromBool x = NFPredicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance Negatable (NormalSentence v p f) where
+    negatePrivate = NFNot
+    foldNegation normal inverted (NFNot x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+{-
+instance (Arity p, Constants p, Combinable (NormalSentence v p f)) => Pred p (NormalTerm v f) (NormalSentence v p f) where
+    pApp0 x = NFPredicate x []
+    pApp1 x a = NFPredicate x [a]
+    pApp2 x a b = NFPredicate x [a,b]
+    pApp3 x a b c = NFPredicate x [a,b,c]
+    pApp4 x a b c d = NFPredicate x [a,b,c,d]
+    pApp5 x a b c d e = NFPredicate x [a,b,c,d,e]
+    pApp6 x a b c d e f = NFPredicate x [a,b,c,d,e,f]
+    pApp7 x a b c d e f g = NFPredicate x [a,b,c,d,e,f,g]
+    x .=. y = NFEqual x y
+    x .!=. y = NFNot (NFEqual x y)
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Variable v, Predicate p, Function f v, Combinable (NormalSentence v p f)) => Pretty (NormalSentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Predicate p, Function f v, Combinable (NormalSentence v p f)) => Formula (NormalSentence v p f) (NormalSentence v p f) where
+    atomic x@(NFPredicate _ _) = x
+    atomic x@(NFEqual _ _) = x
+    atomic _ = error "Chiou: atomic"
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f), Combinable (NormalSentence v p f), Term (NormalTerm v f) v f,
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (NormalSentence v p f) (NormalSentence v p f) v where
+    for_all _ _ = error "FirstOrderFormula NormalSentence"
+    exists _ _ = error "FirstOrderFormula NormalSentence"
+    foldFirstOrder _ co tf at f =
+        case f of
+          NFNot x -> co ((:~:) x)
+          NFEqual _ _ -> at f
+          NFPredicate p _ -> maybe (at f) tf (asBool p)
+{-
+    zipFirstOrder _ co tf at f1 f2 =
+        case (f1, f2) of
+          (NFNot f1', NFNot f2') -> co ((:~:) f1') ((:~:) f2')
+          (NFEqual _ _, NFEqual _ _) -> at f1 f2
+          (NFPredicate _ _, NFPredicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Combinable (NormalSentence v p f), Predicate p, Function f v, Variable v) => HasFixity (NormalSentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Variable v, Function f v) => Term (NormalTerm v f) v f where
+    vt = NormalVariable
+    fApp = NormalFunction
+    foldTerm v f t =
+            case t of
+              NormalVariable x -> v x
+              NormalFunction x ts -> f x ts
+    zipTerms v fn t1 t2 =
+        case (t1, t2) of
+          (NormalVariable x1, NormalVariable x2) -> v x1 x2
+          (NormalFunction f1 ts1, NormalFunction f2 ts2) -> fn f1 ts1 f2 ts2
+          _ -> Nothing
+
+toSentence :: (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Atom (Sentence v p f) (CTerm v f) v, Function f v, Variable v, Predicate p) =>
+              NormalSentence v p f -> Sentence v p f
+toSentence (NFNot s) = (.~.) (toSentence s)
+toSentence (NFEqual t1 t2) = toTerm t1 .=. toTerm t2
+toSentence (NFPredicate p ts) = pApp p (map toTerm ts)
+
+toTerm :: (Variable v, Function f v) => NormalTerm v f -> CTerm v f
+toTerm (NormalFunction f ts) = fApp f (map toTerm ts)
+toTerm (NormalVariable v) = vt v
+
+fromSentence :: forall v p f. (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Predicate p) =>
+                Sentence v p f -> NormalSentence v p f
+fromSentence = foldFirstOrder 
+                 (\ _ _ _ -> error "fromSentence 1")
+                 (\ cm ->
+                      case cm of
+                        ((:~:) f) -> NFNot (fromSentence f)
+                        _ -> error "fromSentence 2")
+                 (\ x -> NFPredicate (fromBool x) [])
+                 (foldAtomEq (\ p ts -> NFPredicate p (map fromTerm ts))
+                             (\ x -> NFPredicate (fromBool x) [])
+                             (\ t1 t2 -> NFEqual (fromTerm t1) (fromTerm t2)))
+
+fromTerm :: CTerm v f -> NormalTerm v f
+fromTerm (Function f ts) = NormalFunction f (map fromTerm ts)
+fromTerm (Variable v) = NormalVariable v
diff --git a/testdata/split-merge-expected/Data/Logic/Instances/PropLogic.hs b/testdata/split-merge-expected/Data/Logic/Instances/PropLogic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Instances/PropLogic.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.PropLogic
+    ( flatten
+    , plSat0
+    , plSat
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (clauseNormalForm', fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set.Extra as S (Set, toList)
+import PropLogic (PropAlg(satisfiable), PropForm(..))
+
+instance Negatable (PropForm a) where
+    negatePrivate = N
+    foldNegation normal inverted (N x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance {- Ord a => -} Combinable (PropForm a) where
+    x .<=>. y = EJ [x, y]
+    x .=>.  y = SJ [x, y]
+    x .|.   y = DJ [x, y]
+    x .&.   y = CJ [x, y]
+
+instance (Pretty a, HasFixity a, Ord a) => Formula (PropForm a) a where
+    atomic = A
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (PropForm a), Pretty a, HasFixity a, Ord a) => PropositionalFormula (PropForm a) a where
+    foldPropositional co tf at formula =
+        case formula of
+          -- EJ [x,y,z,...] -> CJ [EJ [x,y], EJ[y,z], ...]
+          EJ [] -> error "Empty equijunct"
+          EJ [x] -> foldPropositional co tf at x
+          EJ [x0, x1] -> co (BinOp x0 (:<=>:) x1)
+          EJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> EJ [x0, x1]) (pairs xs)))
+          SJ [] -> error "Empty subjunct"
+          SJ [x] -> foldPropositional co tf at x
+          SJ [x0, x1] -> co (BinOp x0 (:=>:) x1)
+          SJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> SJ [x0, x1]) (pairs xs)))
+          DJ [] -> tf False
+          DJ [x] -> foldPropositional co tf at x
+          DJ (x0:xs) -> co (BinOp x0 (:|:) (DJ xs))
+          CJ [] -> tf True
+          CJ [x] -> foldPropositional co tf at x
+          CJ (x0:xs) -> co (BinOp x0 (:&:) (CJ xs))
+          N x -> co ((:~:) x)
+          -- Not sure what to do about these - so far not an issue.
+          T -> tf True
+          F -> tf False
+          A x -> at x
+
+instance Constants (PropForm formula) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (PropositionalFormula (PropForm atom) atom, Pretty atom, HasFixity atom) => Pretty (PropForm atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (PropositionalFormula (PropForm atom) atom, HasFixity atom) => HasFixity (PropForm atom) where
+    fixity = fixityPropositional
+
+pairs :: [a] -> [(a, a)]
+pairs (x:y:zs) = (x,y) : pairs (y:zs)
+pairs _ = []
+
+flatten :: PropForm a -> PropForm a
+flatten (CJ xs) =
+    CJ (concatMap f (map flatten xs))
+    where
+      f (CJ ys) = ys
+      f x = [x]
+flatten (DJ xs) =
+    DJ (concatMap f (map flatten xs))
+    where
+      f (DJ ys) = ys
+      f x = [x]
+flatten (EJ xs) = EJ (map flatten xs)
+flatten (SJ xs) = SJ (map flatten xs)
+flatten (N x) = N (flatten x)
+flatten x = x
+
+plSat0 :: (PropAlg a (PropForm formula), PropositionalFormula formula atom, Ord formula) => PropForm formula -> Bool
+plSat0 f = satisfiable . (\ (x :: PropForm formula) -> x) . clauses0 $ f
+
+clauses0 :: (PropositionalFormula formula atom, Ord formula) => PropForm formula -> PropForm formula
+clauses0 f = CJ . map DJ . map S.toList . S.toList $ clauseNormalForm' f
+
+plSat :: forall m formula atom term v f. (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+                formula -> SkolemT v term m Bool
+plSat f = clauses f >>= (\ (x :: PropForm formula) -> return x) >>= return . satisfiable
+
+clauses :: forall m formula atom term v f.
+           (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+           formula -> SkolemT v term m (PropForm formula)
+clauses f =
+    do (cnf :: S.Set (S.Set formula)) <- clauseNormalForm f
+       return . CJ . map DJ . map (map A) . map S.toList . S.toList $ cnf
diff --git a/testdata/split-merge-expected/Data/Logic/Instances/SatSolver.hs b/testdata/split-merge-expected/Data/Logic/Instances/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Instances/SatSolver.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.SatSolver where
+
+import Control.Monad.State (get, put)
+import Control.Monad.Trans (lift)
+import Data.Boolean.SatSolver (assertTrue', CNF, Literal(..), newSatSolver, solve)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.ClauseNormalForm (ClauseNormalFormula(..))
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import qualified Data.Logic.Classes.Literal.Literal as N (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable(..), negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import Data.Logic.Normal.Implicative (LiteralMapT, NormalT)
+import qualified Data.Map as M (insert, lookup)
+import qualified Data.Set.Extra as S (fromList, ssMapM, toList)
+
+instance Ord Literal where
+    compare (Neg _) (Pos _) = LT
+    compare (Pos _) (Neg _) = GT
+    compare (Pos m) (Pos n) = compare m n
+    compare (Neg m) (Neg n) = compare m n
+
+instance Negatable Literal where
+    negatePrivate (Neg x) = Pos x
+    negatePrivate (Pos x) = Neg x
+    foldNegation _ inverted (Neg x) = inverted (Pos x)
+    foldNegation normal _ (Pos x) = normal (Pos x)
+
+deriving instance Data Literal
+deriving instance Typeable Literal
+
+instance ClauseNormalFormula CNF Literal where
+    clauses = S.fromList . map S.fromList
+    makeCNF = map S.toList . S.toList
+    satisfiable cnf = return . not . null $ assertTrue' cnf newSatSolver >>= solve
+
+toCNF :: (Monad m,
+          FirstOrderFormula formula atom v,
+          PropositionalFormula formula atom,
+          Atom atom term v,
+          AtomEq atom p term,
+          Term term v f,
+          N.Literal formula atom,
+          Ord formula) =>
+         formula -> NormalT formula v term m CNF
+toCNF f = clauseNormalForm f >>= S.ssMapM (lift . toLiteral) >>= return . makeCNF
+
+-- |Convert a [[formula]] to CNF, which means building a map from
+-- formula to Literal.
+toLiteral :: forall m lit. (Monad m, Negatable lit, Ord lit) =>
+             lit -> LiteralMapT lit m Literal
+toLiteral f =
+    literalNumber >>= return . if negated f then Neg else Pos
+    where
+      literalNumber :: LiteralMapT lit m Int
+      literalNumber =
+          get >>= \ (count, m) ->
+          case M.lookup f' m of
+            Nothing -> do let m' = M.insert f' count m
+                          put (count+1, m') 
+                          return count
+            Just n -> return n
+      f' :: lit
+      f' = if negated f then (.~.) f else f
diff --git a/testdata/split-merge-expected/Data/Logic/KnowledgeBase.hs b/testdata/split-merge-expected/Data/Logic/KnowledgeBase.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/KnowledgeBase.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, PackageImports,
+             RankNTypes, TemplateHaskell, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall #-}
+
+{- KnowledgeBase.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.KnowledgeBase
+    ( WithId(WithId, wiItem, wiIdent) -- Probably only used by some unit tests, and not really correctly
+    , ProverT
+    , runProver'
+    , runProverT'
+    , getKB
+    , unloadKB
+    -- , deleteKB
+    , askKB
+    , theoremKB
+    , inconsistantKB
+    , ProofResult(Proved, Disproved, Invalid)
+    , Proof(Proof, proofResult, proof)
+    , validKB
+    , tellKB
+    , loadKB
+    , showKB
+    ) where
+
+import "mtl" Control.Monad.Identity (Identity(runIdentity))
+import "mtl" Control.Monad.State (evalStateT, MonadState(get, put), StateT)
+import "mtl" Control.Monad.Trans (lift)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm)
+import Data.Logic.Resolution (getSetOfSupport, prove, SetOfSupport)
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as S (empty, map, minView, null, partition, Set, union)
+import Prelude hiding (negate)
+
+type SentenceCount = Int
+
+data WithId a = WithId {wiItem :: a, wiIdent :: Int} deriving (Eq, Ord, Show, Data, Typeable)
+
+withId :: Int -> a -> WithId a
+withId i x = WithId {wiIdent = i, wiItem = x}
+
+{-
+withIdPairs :: [WithId a] -> [(a, Int)]
+withIdPairs = map (\ x -> (wiItem x, wiIdent x))
+
+wiLookupId :: Eq a => a -> [WithId a] -> Maybe Int
+wiLookupId x xs = lookup x (withIdPairs xs)
+
+withIdPairs' :: [WithId a] -> [(Int, a)]
+withIdPairs' = map (\ x -> (wiIdent x, wiItem x))
+
+wiLookupItem :: Int -> [WithId a] -> Maybe a
+wiLookupItem i xs = lookup i (withIdPairs' xs)
+-}
+
+type KnowledgeBase inf = S.Set (WithId inf)
+
+data ProverState inf
+    = ProverState
+      { recursionLimit :: Maybe Int
+      , knowledgeBase :: KnowledgeBase inf
+      , sentenceCount :: Int }
+
+zeroKB :: Maybe Int -> ProverState inf
+zeroKB limit =
+    ProverState
+         { recursionLimit = limit
+         , knowledgeBase = S.empty
+         , sentenceCount = 1 }
+
+-- |A monad for running the knowledge base.
+type ProverT inf = StateT (ProverState inf)
+type ProverT' v term inf m a = ProverT inf (SkolemT v term m) a
+
+runProverT' :: Monad m => Maybe Int -> ProverT' v term inf m a -> m a
+runProverT' limit = runSkolemT . runProverT limit
+runProverT :: Monad m => Maybe Int -> StateT (ProverState inf) m a -> m a
+runProverT limit action = evalStateT action (zeroKB limit)
+runProver' :: Maybe Int -> ProverT' v term inf Identity a -> a
+runProver' limit = runIdentity . runProverT' limit
+{-
+runProver :: StateT (ProverState inf) Identity a -> a
+runProver = runIdentity . runProverT
+-}
+
+data ProofResult
+    = Disproved
+    -- ^ The conjecture is unsatisfiable
+    | Proved
+    -- ^ The negated conjecture is unsatisfiable
+    | Invalid
+    -- ^ Both are satisfiable
+    deriving (Data, Typeable, Eq, Ord, Show)
+
+$(deriveSafeCopy 1 'base ''ProofResult)
+
+data Proof lit = Proof {proofResult :: ProofResult, proof :: S.Set (ImplicativeForm lit)} deriving (Data, Typeable, Eq, Ord)
+
+instance (Ord lit, Show lit, Literal lit atom, FirstOrderFormula lit atom v) => Show (Proof lit) where
+    show p = "Proof {proofResult = " ++ show (proofResult p) ++ ", proof = " ++ show (proof p) ++ "}"
+
+-- |Remove a particular sentence from the knowledge base
+unloadKB :: (Monad m, Ord inf) => SentenceCount -> ProverT inf m (Maybe (KnowledgeBase inf))
+unloadKB n =
+    do st <- get
+       let (discard, keep) = S.partition ((== n) . wiIdent) (knowledgeBase st)
+       put (st {knowledgeBase = keep}) >> return (Just discard)
+
+-- |Return the contents of the knowledgebase.
+getKB :: Monad m => ProverT inf m (S.Set (WithId inf))
+getKB = get >>= return . knowledgeBase
+
+-- |Return a flag indicating whether sentence was disproved, along
+-- with a disproof.
+inconsistantKB :: forall m formula atom term v p f lit.
+                  (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+                   Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+                  formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+inconsistantKB s =
+    get >>= \ st ->
+    lift (implicativeNormalForm s) >>=
+    return . getSetOfSupport >>= \ sos ->
+    getKB >>=
+    return . prove (recursionLimit st) S.empty sos . S.map wiItem
+
+-- |Return a flag indicating whether sentence was proved, along with a
+-- proof.
+theoremKB :: forall m formula atom term v p f lit.
+             (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+              Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+             formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+theoremKB s = inconsistantKB ((.~.) s)
+
+-- |Try to prove a sentence, return the result and the proof.
+-- askKB should be in KnowledgeBase module. However, since resolution
+-- is here functions are here, it is also placed in this module.
+askKB :: (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+          Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+         formula -> ProverT' v term (ImplicativeForm lit) m Bool
+askKB s = theoremKB s >>= return . fst
+
+-- |See whether the sentence is true, false or invalid.  Return proofs
+-- for truth and falsity.
+validKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+            Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+           formula -> ProverT' v term (ImplicativeForm lit) m (ProofResult, SetOfSupport lit v term, SetOfSupport lit v term)
+validKB s =
+    theoremKB s >>= \ (proved, proof1) ->
+    inconsistantKB s >>= \ (disproved, proof2) ->
+    return (if proved then Proved else if disproved then Disproved else Invalid, proof1, proof2)
+
+-- |Validate a sentence and insert it into the knowledgebase.  Returns
+-- the INF sentences derived from the new sentence, or Nothing if the
+-- new sentence is inconsistant with the current knowledgebase.
+tellKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+          formula -> ProverT' v term (ImplicativeForm lit) m (Proof lit)
+tellKB s =
+    do st <- get
+       inf <- lift (implicativeNormalForm s)
+       let inf' = S.map (withId (sentenceCount st)) inf
+       (valid, _, _) <- validKB s
+       case valid of
+         Disproved -> return ()
+         _ -> put st { knowledgeBase = S.union (knowledgeBase st) inf'
+                     , sentenceCount = sentenceCount st + 1 }
+       return $ Proof {proofResult = valid, proof = S.map wiItem inf'}
+
+loadKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+          [formula] -> ProverT' v term (ImplicativeForm lit) m [Proof lit]
+loadKB sentences = mapM tellKB sentences
+
+-- |Delete an entry from the KB.
+{-
+deleteKB :: Monad m => Int -> ProverT inf m String
+deleteKB i = do st <- get
+                modify (\ st' -> st' {knowledgeBase = deleteElement i (knowledgeBase st')})
+                st' <- get
+		return (if length (knowledgeBase st') /= length (knowledgeBase st) then
+			  "Deleted"
+			else
+			  "Failed to delete")
+	     
+deleteElement :: Int -> [a] -> [a]
+deleteElement i l
+    | i <= 0    = l
+    | otherwise = let
+		    (p1, p2) = splitAt (i - 1) l
+		  in
+		    p1 ++ (case p2 of
+			       [] -> []
+			       _ -> tail p2)
+-}
+
+-- |Return a text description of the contents of the knowledgebase.
+showKB :: (Show inf, Monad m) => ProverT inf m String
+showKB = get >>= return . reportKB
+
+reportKB :: (Show inf) => ProverState inf -> String
+reportKB st@(ProverState {knowledgeBase = kb}) =
+    case S.minView kb of
+      Nothing -> "Nothing in Knowledge Base\n"
+      Just (WithId {wiItem = x, wiIdent = n}, kb')
+          | S.null kb' ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n"
+          | True ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n" ++ reportKB (st {knowledgeBase = kb'})
diff --git a/testdata/split-merge-expected/Data/Logic/Normal/Clause.hs b/testdata/split-merge-expected/Data/Logic/Normal/Clause.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Normal/Clause.hs
@@ -0,0 +1,94 @@
+-- |A series of transformations to convert first order logic formulas
+-- into (ultimately) Clause Normal Form.
+-- 
+-- @
+-- 1st order formula:
+--   ∀Y (∀X (taller(Y,X) | wise(X)) => wise(Y))
+-- 
+-- Simplify
+--   ∀Y (~∀X (taller(Y,X) | wise(X)) | wise(Y))
+-- 
+-- Move negations in - Negation Normal Form
+--   ∀Y (∃X (~taller(Y,X) & ~wise(X)) | wise(Y))
+-- 
+-- Move quantifiers out - Prenex Normal Form
+--   ∀Y (∃X ((~taller(Y,X) & ~wise(X)) | wise(Y)))
+-- 
+-- Distribute disjunctions
+--   ∀Y ∃X ((~taller(Y,X) | wise(Y)) & (~wise(X) | wise(Y)))
+-- 
+-- Skolemize  - Skolem Normal Form
+--   ∀Y (~taller(Y,x(Y)) | wise(Y)) & (~wise(x(Y)) | wise(Y))
+-- 
+-- Convert to CNF
+--   { ~taller(Y,x(Y)) | wise(Y),
+--     ~wise(x(Y)) | wise(Y) } 
+-- @
+-- 
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Clause
+    ( clauseNormalForm
+    , cnfTrace
+    ) where
+
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq, prettyAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder)
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Literal.PrettyLit (prettyLit)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Normal (simpcnf')
+import Data.Logic.Harrison.Skolem (nnf, pnf, simplify, skolemize, SkolemT)
+import qualified Data.Set.Extra as Set (Set, toList)
+import Text.PrettyPrint (($$), brackets, Doc, hcat, nest, render, text, vcat)
+
+-- |Convert to Skolem Normal Form and then distribute the disjunctions over the conjunctions:
+-- 
+-- @
+-- Formula      Rewrites to
+-- P | (Q & R)  (P | Q) & (P | R)
+-- (Q & R) | P  (Q | P) & (R | P)
+-- @
+-- 
+clauseNormalForm :: forall formula atom term v f lit m.
+                    (Monad m,
+                     FirstOrderFormula formula atom v,
+                     PropositionalFormula formula atom,
+                     Atom atom term v,
+                     Term term v f,
+                     Literal lit atom,
+                     Ord formula, Ord lit) =>
+                    formula -> SkolemT v term m (Set.Set (Set.Set lit))
+clauseNormalForm fm = skolemize id fm >>= return . (simpcnf' :: formula -> Set.Set (Set.Set lit))
+
+cnfTrace :: forall m formula atom term v p f lit.
+            (Monad m,
+             FirstOrderFormula formula atom v,
+             PropositionalFormula formula atom,
+             Atom atom term v,
+             AtomEq atom p term,
+             Term term v f,
+             Literal lit atom,
+             Ord formula, Ord lit) =>
+            (v -> Doc)
+         -> (p -> Doc)
+         -> (f -> Doc)
+         -> formula
+         -> SkolemT v term m (String, Set.Set (Set.Set lit))
+cnfTrace pv pp pf f =
+    do (snf :: formula) <- skolemize id f
+       cnf <- clauseNormalForm f
+       return (render (vcat
+                       [text "Original:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 f),
+                        text "Simplified:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (simplify f)),
+                        text "Negation Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (nnf . simplify $ f)),
+                        text "Prenex Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (pnf f)),
+                        text "Skolem Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 snf),
+                        text "Clause Normal Form:" $$ vcat (map prettyClause (fromSS cnf))]), cnf)
+    where
+      prettyClause (clause :: [lit]) =
+          nest 2 . brackets . hcat . intersperse (text ", ") . map (nest 2 . brackets . prettyLit (prettyAtomEq pv pp pf) pv 0) $ clause
+      fromSS = (map Set.toList) . Set.toList 
diff --git a/testdata/split-merge-expected/Data/Logic/Normal/Implicative.hs b/testdata/split-merge-expected/Data/Logic/Normal/Implicative.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Normal/Implicative.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE DeriveDataTypeable, PackageImports, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Implicative
+    ( LiteralMapT
+    , NormalT
+    , runNormal
+    , runNormalT
+    , ImplicativeForm(INF, neg, pos)
+    , makeINF'
+    , implicativeNormalForm
+    , prettyINF
+    , prettyProof
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (MonadPlus, msum, StateT(runStateT))
+import Data.Generics (Data, listify, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (ifElse, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Skolem (Skolem(isSkolem))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (empty, flatten, fold, fromList, insert, map, Set, singleton, toList)
+import Text.PrettyPrint (cat, Doc, hsep, text)
+
+-- |Combination of Normal monad and LiteralMap monad
+type NormalT formula v term m a = SkolemT v term (LiteralMapT formula m) a
+
+runNormalT :: Monad m => NormalT formula v term m a -> m a
+runNormalT action = runLiteralMapM (runSkolemT action)
+
+runNormal :: NormalT formula v term Identity a -> a
+runNormal = runIdentity . runNormalT
+ 
+--type LiteralMap f = LiteralMapT f Identity
+type LiteralMapT f = StateT (Int, Map.Map f Int)
+
+--runLiteralMap :: LiteralMap p a -> a
+--runLiteralMap action = runIdentity (runLiteralMapM action)
+
+runLiteralMapM :: Monad m => LiteralMapT f m a -> m a
+runLiteralMapM action = (runStateT action) (1, Map.empty) >>= return . fst
+
+-- |A type to represent a formula in Implicative Normal Form.  Such a
+-- formula has the form @a & b & c .=>. d | e | f@, where a thru f are
+-- literals.  One more restriction that is not implied by the type is
+-- that no literal can appear in both the pos set and the neg set.
+data ImplicativeForm lit =
+    INF {neg :: Set.Set lit, pos :: Set.Set lit}
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+-- |A version of MakeINF that takes lists instead of sets, used for
+-- implementing a more attractive show method.
+makeINF' :: (Negatable lit, Ord lit) => [lit] -> [lit] -> ImplicativeForm lit
+makeINF' n p = INF (Set.fromList n) (Set.fromList p)
+
+prettyINF :: (Negatable lit, Ord lit) => (lit -> Doc) -> ImplicativeForm lit -> Doc
+prettyINF lit x = cat $ [text "(", hsep (map lit (Set.toList (neg x))),
+                         text ") => (", hsep (map lit (Set.toList (pos x))), text ")"]
+
+prettyProof :: (Negatable lit, Ord lit) => (lit -> Doc) -> Set.Set (ImplicativeForm lit) -> Doc
+prettyProof lit p = cat $ [text "["] ++ intersperse (text ", ") (map (prettyINF lit) (Set.toList p)) ++ [text "]"]
+
+-- |Take the clause normal form, and turn it into implicative form,
+-- where each clauses becomes an (LHS, RHS) pair with the negated
+-- literals on the LHS and the non-negated literals on the RHS:
+-- @
+--   (a | ~b | c | ~d) becomes (b & d) => (a | c)
+--   (~b | ~d) | (a | c)
+--   ~~(~b | ~d) | (a | c)
+--   ~(b & d) | (a | c)
+-- @
+-- If there are skolem functions on the RHS, split the formula using
+-- this identity:
+-- @
+--   (a | b | c) => (d & e & f)
+-- @
+-- becomes
+-- @
+--    a | b | c => d
+--    a | b | c => e
+--    a | b | c => f
+-- @
+implicativeNormalForm :: forall m formula atom term v f lit. 
+                         (Monad m,
+                          FirstOrderFormula formula atom v,
+                          PropositionalFormula formula atom,
+                          Atom atom term v,
+                          Literal lit atom,
+                          Term term v f,
+                          Data formula, Ord formula, Ord lit, Data lit, Skolem f v) =>
+                         formula -> SkolemT v term m (Set.Set (ImplicativeForm lit))
+implicativeNormalForm formula =
+    do cnf <- clauseNormalForm formula
+       let pairs = Set.map (Set.fold collect (Set.empty, Set.empty)) cnf :: Set.Set (Set.Set lit, Set.Set lit)
+           pairs' = Set.flatten (Set.map split pairs) :: Set.Set (Set.Set lit, Set.Set lit)
+       return (Set.map (\ (n,p) -> INF n p) pairs')
+    where
+      collect :: lit -> (Set.Set lit, Set.Set lit) -> (Set.Set lit, Set.Set lit)
+      collect f (n, p) =
+          foldLiteral (\ f' -> (Set.insert f' n, p))
+                      (ifElse (n, Set.insert true p) (Set.insert true n, p))
+                      (\ _ -> (n, Set.insert f p))
+                      f
+      split :: (Set.Set lit, Set.Set lit) -> Set.Set (Set.Set lit, Set.Set lit)
+      split (lhs, rhs) =
+          if any isSkolem (gFind rhs :: [f])
+          then Set.map (\ x -> (lhs, Set.singleton x)) rhs
+          else Set.singleton (lhs, rhs)
+
+-- | @gFind a@ will extract any elements of type @b@ from
+-- @a@'s structure in accordance with the MonadPlus
+-- instance, e.g. Maybe Foo will return the first Foo
+-- found while [Foo] will return the list of Foos found.
+gFind :: (MonadPlus m, Data a, Typeable b) => a -> m b
+gFind = msum . map return . listify (const True)
diff --git a/testdata/split-merge-expected/Data/Logic/Resolution.hs b/testdata/split-merge-expected/Data/Logic/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Resolution.hs
@@ -0,0 +1,374 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS -Wall -Wwarn #-}
+
+{- Resolution.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.Resolution
+    ( prove
+    , getSetOfSupport
+    , SetOfSupport
+    , Unification
+    , isRenameOfAtomEq
+    , getSubstAtomEq
+    ) where
+
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Atom (Atom(isRename, getSubst))
+import Data.Logic.Classes.Constants (fromBool)
+import Data.Logic.Classes.Equals (applyEq, AtomEq(foldAtomEq, equals), zipAtomsEq)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Literal.ZipLiterals (zipLiterals)
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Normal.Implicative (ImplicativeForm(INF, neg, pos))
+import Data.Map (empty, Map)
+import qualified Data.Map as Map (insert, insertWith, intersection, lookup, Map, union, unionWith)
+import Data.Maybe (isJust)
+import qualified Data.Set.Extra as S (any, catMaybes, deleteFindMin, empty, insert, map, minView, null, Set, singleton, size, toList, union, unions)
+
+type SetOfSupport lit v term = S.Set (Unification lit v term)
+
+type Unification lit v term = (ImplicativeForm lit, Map.Map v term)
+
+prove :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, {-Show v, Show term,-} AtomEq atom p term, Predicate p) =>
+         Maybe Int -- ^ Recursion limit.  We continue recursing until this
+                   -- becomes zero.  If it is negative it may recurse until
+                   -- it overflows the stack.
+      -> SetOfSupport lit v term
+      -> SetOfSupport lit v term
+      -> S.Set (ImplicativeForm lit)
+      -> (Bool, SetOfSupport lit v term)
+prove (Just 0) ss1 _ _ = (False, ss1)
+prove limit ss1 ss2' kb  =
+    case S.minView ss2' of
+      Nothing -> (False, ss1)
+      Just (s, ss2) ->
+          case prove' s kb ss2 ss1 of
+            (ss', True) -> (True, (S.insert s (S.union ss1 ss')))
+            (ss', False) -> prove (maybe Nothing (\ n -> Just (n - 1)) limit) (S.insert s ss1) ss' (S.insert (fst s) kb)
+-- prove ss1 [] _kb = (False, ss1)
+-- prove ss1 (s:ss2) kb =
+--     let
+--       (ss', tf) = prove' s kb ss2 ss1
+--     in
+--       if tf then
+--         (True, (ss1 ++ [s] ++ss'))
+--       else
+--         prove (ss1 ++ [s]) ss' (fst s:kb)
+
+prove' :: forall lit atom p f v term.
+          (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+          Unification lit v term -> S.Set (ImplicativeForm lit) -> SetOfSupport lit v term -> SetOfSupport lit v term -> (SetOfSupport lit v term, Bool)
+prove' p kb ss1 ss2 =
+    let
+      res1 = S.map (\x -> resolution p (x, empty)) kb
+      res2 = S.map (\x -> resolution (x, empty) p) kb
+      dem1 = S.map (\e -> demodulate p (e, empty)) kb
+      dem2 = S.map (\p' -> demodulate (p', empty) p) kb
+      (ss', tf) = getResult (S.union ss1 ss2) (S.unions [res1, res2, dem1, dem2])
+    in
+      if S.null ss' then (ss1, False) else (S.union ss1 ss', tf)
+
+getResult :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+             SetOfSupport lit v term -> S.Set (Maybe (Unification lit v term)) -> ((SetOfSupport lit v term), Bool)
+getResult ss us =
+    case S.minView us of
+      Nothing ->
+          (S.empty, False)
+      Just (Nothing, xs) ->
+          getResult ss xs
+      Just ((Just x@(inf, _v)), xs) ->
+          if S.null (neg inf) && S.null (pos inf)
+          then (S.singleton x, True)
+          else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+               then getResult ss xs
+               else let (xs', tf) = getResult ss xs in (S.insert x xs', tf)
+{-
+getResult _ [] = (S.empty, False)
+getResult ss (Nothing:xs) = getResult ss xs
+getResult ss ((Just x):xs)  =
+    if S.null (neg inf) && S.null (pos inf)
+    then (S.singleton x, True)
+    else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+         then getResult ss xs
+         else let (xs', tf) = getResult ss xs in (S.insert x xs' tf)
+    where
+      (inf, _v) = x
+-}
+
+-- |Convert the "question" to a set of support.
+getSetOfSupport :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v) =>
+                   S.Set (ImplicativeForm lit) -> S.Set (ImplicativeForm lit, Map.Map v term)
+getSetOfSupport s = S.map (\ x -> (x, getSubsts x empty)) s
+
+getSubsts :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+             ImplicativeForm lit -> Map.Map v term -> Map.Map v term
+getSubsts inf theta =
+    getSubstSentences (pos inf) (getSubstSentences (neg inf) theta)
+
+getSubstSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> Map.Map v term -> Map.Map v term
+getSubstSentences xs theta = foldr getSubstSentence theta (S.toList xs)
+
+
+getSubstSentence :: (Literal lit atom, Atom atom term v, Term term v f)  => lit -> Map.Map v term -> Map.Map v term
+getSubstSentence formula theta =
+    foldLiteral
+          (\ s -> getSubstSentence s theta)
+          (const theta)
+          (getSubst theta)
+          formula
+
+getSubstAtomEq :: forall atom p term v f. (AtomEq atom p term, Term term v f) => Map v term -> atom -> Map v term
+getSubstAtomEq theta = foldAtomEq (\ _ ts -> getSubstsTerms ts theta) (const theta) (\ t1 t2 -> getSubstsTerms [t1, t2] theta)
+
+getSubstsTerms :: Term term v f => [term] -> Map.Map v term -> Map.Map v term
+getSubstsTerms [] theta = theta
+getSubstsTerms (x:xs) theta =
+    let
+      theta' = getSubstsTerm x theta
+      theta'' = getSubstsTerms xs theta'
+    in
+      theta''
+
+getSubstsTerm :: Term term v f => term -> Map.Map v term -> Map.Map v term
+getSubstsTerm term theta =
+    foldTerm (\ v -> Map.insertWith (\ _ old -> old) v (vt v) theta)
+             (\ _ ts -> getSubstsTerms ts theta)
+             term
+
+isRenameOf :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+              ImplicativeForm lit -> ImplicativeForm lit -> Bool
+isRenameOf inf1 inf2 =
+    (isRenameOfSentences lhs1 lhs2) && (isRenameOfSentences rhs1 rhs2)
+    where
+      lhs1 = neg inf1
+      rhs1 = pos inf1
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+isRenameOfSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> S.Set lit -> Bool
+isRenameOfSentences xs1 xs2 =
+    S.size xs1 == S.size xs2 && all (uncurry isRenameOfSentence) (zip (S.toList xs1) (S.toList xs2))
+
+isRenameOfSentence :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => lit -> lit -> Bool
+isRenameOfSentence f1 f2 =
+    maybe False id $
+    zipLiterals (\ _ _ -> Just False) (\ x y -> Just (x == y)) (\ x y -> Just (isRename x y)) f1 f2
+
+isRenameOfAtomEq :: (AtomEq atom p term, Term term v f) => atom -> atom -> Bool
+isRenameOfAtomEq a1 a2 =
+    maybe False id $
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> Just (p1 == p2 && isRenameOfTerms ts1 ts2))
+               (\ x y -> Just (x == y))
+               (\ t1l t1r t2l t2r -> Just (isRenameOfTerm t1l t2l && isRenameOfTerm t1r t2r))
+               a1 a2
+
+isRenameOfTerm :: Term term v f => term -> term -> Bool
+isRenameOfTerm t1 t2 =
+    maybe False id $
+    zipTerms (\ _ _ -> Just True)
+             (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && isRenameOfTerms ts1 ts2))
+             t1 t2
+
+isRenameOfTerms :: Term term v f => [term] -> [term] -> Bool
+isRenameOfTerms ts1 ts2 =
+    if length ts1 == length ts2 then
+      let
+        tsTuples = zip ts1 ts2
+      in
+        foldl (&&) True (map (\(t1, t2) -> isRenameOfTerm t1 t2) tsTuples)
+    else
+      False
+
+resolution :: forall lit atom p f term v. (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term, Eq p) =>
+             (ImplicativeForm lit, Map.Map v term) -> (ImplicativeForm lit, Map.Map v term) -> Maybe (ImplicativeForm lit, Map v term)
+resolution (inf1, theta1) (inf2, theta2) =
+    let
+        lhs1 = neg inf1
+        rhs1 = pos inf1
+        lhs2 = neg inf2
+        rhs2 = pos inf2
+        unifyResult = tryUnify rhs1 lhs2
+    in
+      case unifyResult of
+        Just ((rhs1', theta1'), (lhs2', theta2')) ->
+            let
+              lhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') lhs1)
+                              (S.catMaybes $ S.map (\s -> subst s theta2') lhs2')
+              rhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') rhs1')
+                              (S.catMaybes $ S.map (\s -> subst s theta2') rhs2)
+              theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2')
+            in
+              Just (INF lhs'' rhs'', theta)
+        Nothing -> Nothing
+    where
+      tryUnify :: (Literal lit atom, Ord lit) =>
+                  S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify lhs rhs = tryUnify' lhs rhs S.empty
+                         
+      tryUnify' :: (Literal lit atom, Ord lit) =>
+                   S.Set lit -> S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify' lhss _ _ | S.null lhss = Nothing
+      tryUnify' lhss'' rhss lhss' =
+          let (lhs, lhss) = S.deleteFindMin lhss'' in
+          case tryUnify'' lhs rhss S.empty of
+            Nothing -> tryUnify' lhss rhss (S.insert lhs lhss')
+            Just (rhss', theta1', theta2') ->
+                Just ((S.union lhss' lhss, theta1'), (rhss', theta2'))
+
+      tryUnify'' :: (Literal lit atom, Ord lit) =>
+                    lit -> S.Set lit -> S.Set lit -> Maybe (S.Set lit, Map.Map v term, Map.Map v term)
+      tryUnify'' _x rhss _ | S.null rhss = Nothing
+      tryUnify'' x rhss'' rhss' =
+          let (rhs, rhss) = S.deleteFindMin rhss'' in
+          case unify x rhs of
+            Nothing -> tryUnify'' x rhss (S.insert rhs rhss')
+            Just (theta1', theta2') -> Just (S.union rhss' rhss, theta1', theta2')
+
+-- |Try to unify the second argument using the equality in the first.
+demodulate :: (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term) =>
+              (Unification lit v term) -> (Unification lit v term) -> Maybe (Unification lit v term)
+demodulate (inf1, theta1) (inf2, theta2) =
+    case (S.null (neg inf1), S.toList (pos inf1)) of
+      (True, [lit1]) ->
+          foldLiteral (\ _ -> error "demodulate") (\ _ -> Nothing) (foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) p) lit1
+      _ -> Nothing
+    where
+      p t1 t2 =
+          case findUnify t1 t2 (S.union lhs2 rhs2) of
+            Just ((t1', t2'), theta1', theta2') ->
+                let substNeg2 = S.catMaybes $ S.map (\x -> subst x theta2') lhs2
+                    substPos2 = S.catMaybes $ S.map (\x -> subst x theta2') rhs2
+                    lhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substNeg2
+                    rhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substPos2
+                    theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2') in
+                Just (INF lhs rhs, theta)
+            Nothing -> Nothing
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+-- |Unification: unifies two sentences.
+unify :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+         lit -> lit -> Maybe (Map.Map v term, Map.Map v term)
+unify s1 s2 = unify' s1 s2 empty empty
+
+unify' :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+          lit -> lit -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unify' f1 f2 theta1 theta2 =
+    zipLiterals
+         (\ _ _ -> error "unify'")
+         (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+         (unify2AtomsEq theta1 theta2)
+         f1 f2
+
+unify2AtomsEq :: (AtomEq atom p term, Term term v f) => Map.Map v term -> Map.Map v term -> atom -> atom -> Maybe (Map.Map v term, Map.Map v term)
+unify2AtomsEq theta1 theta2 a1 a2 =
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> if p1 == p2 then unifyTerms ts1 ts2 theta1 theta2 else Nothing)
+               (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+               (\ l1 r1 l2 r2 -> unifyTerms [l1, r1] [l2, r2] theta1 theta2)
+               a1 a2
+
+unifyTerm :: Term term v f => term -> term -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerm t1 t2 theta1 theta2 =
+    foldTerm
+          (\ v1 ->
+               maybe (if vt v1 == t2 then Nothing else Just (Map.insert v1 t2 theta1, theta2))
+                     (\ t1' -> unifyTerm t1' t2 theta1 theta2)
+                     (Map.lookup v1 theta1))
+          (\ f1 ts1 ->
+               foldTerm (\ v2 -> maybe (Just (theta1, Map.insert v2 t1 theta2))
+                                 (\ t2' -> unifyTerm t1 t2' theta1 theta2)
+                                 (Map.lookup v2 theta2))
+                        (\ f2 ts2 -> if f1 == f2
+                                     then unifyTerms ts1 ts2 theta1 theta2
+                                     else Nothing)
+                        t2)
+          t1
+
+unifyTerms :: Term term v f =>
+              [term] -> [term] -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerms [] [] theta1 theta2 = Just (theta1, theta2)
+unifyTerms (t1:ts1) (t2:ts2) theta1 theta2 =
+    case (unifyTerm t1 t2 theta1 theta2) of
+      Nothing                -> Nothing
+      Just (theta1',theta2') -> unifyTerms ts1 ts2 theta1' theta2'
+unifyTerms _ _ _ _ = Nothing
+
+findUnify :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term) =>
+             term -> term -> S.Set lit -> Maybe ((term, term), Map.Map v term, Map.Map v term)
+findUnify tl tr s =
+    let
+      terms = concatMap (foldLiteral (\ (_ :: lit) -> error "getTerms") (\ _ -> []) p) (S.toList s)
+      unifiedTerms' = map (\t -> unifyTerm tl t empty empty) terms
+      unifiedTerms = filter isJust unifiedTerms'
+    in
+     case unifiedTerms of
+       [] -> Nothing
+       (Just (theta1, theta2)):_ ->
+         Just ((substTerm tl theta1, substTerm tr theta1), theta1, theta2)
+       (Nothing:_) -> error "findUnify"
+    where
+      -- getTerms lit = foldLiteral (\ _ -> error "getTerms") p formula
+      p :: atom -> [term]
+      p = foldAtomEq (\ _ ts -> concatMap getTerms' ts) (const []) (\ t1 t2 -> getTerms' t1 ++ getTerms' t2)
+      getTerms' :: term -> [term]
+      getTerms' t = foldTerm (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+
+{-
+getTerms :: Literal formula atom v => formula -> [term]
+getTerms formula =
+    foldLiteral (\ _ -> error "getTerms") p formula
+    where
+      getTerms' t = foldT (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+      p (Equal t1 t2) = getTerms' t1 ++ getTerms' t2
+      p (Apply _ ts) = concatMap getTerms' ts
+-}
+
+replaceTerm :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, Eq term, AtomEq atom p term) => lit -> (term, term) -> Maybe lit
+replaceTerm formula (tl', tr') =
+    foldLiteral
+          (\ _ -> error "error in replaceTerm")
+          (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (map (\ t -> replaceTerm' t) ts))))
+                      (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+                      (\ t1 t2 -> 
+                           let t1' = replaceTerm' t1
+                               t2' = replaceTerm' t2 in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+    where
+      replaceTerm' t =
+          if termEq t tl'
+          then tr'
+          else foldTerm vt (\ f ts -> fApp f (map replaceTerm' ts)) t
+      termEq t1 t2 =
+          maybe False id (zipTerms (\ v1 v2 -> Just (v1 == v2)) (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && length ts1 == length ts2 && all (uncurry termEq) (zip ts1 ts2))) t1 t2)
+
+subst :: (Literal formula atom, AtomEq atom p term, Atom atom term v, Term term v f, Eq term) => formula -> Map.Map v term -> Maybe formula
+subst formula theta =
+    foldLiteral
+          (\ _ -> Just formula)
+          (\ x -> Just (fromBool x))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (substTerms ts theta))))
+                      (Just . fromBool)
+                      (\ t1 t2 ->
+                           let t1' = substTerm t1 theta
+                               t2' = substTerm t2 theta in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+
+substTerm :: Term term v f => term -> Map.Map v term -> term
+substTerm term theta =
+    foldTerm (\ v -> maybe term id (Map.lookup v theta))
+             (\ f ts -> fApp f (substTerms ts theta))
+             term
+
+substTerms :: Term term v f => [term] -> Map.Map v term -> [term]
+substTerms ts theta = map (\t -> substTerm t theta) ts
+
+updateSubst :: Term term v f => Map.Map v term -> Map.Map v term -> Map.Map v term
+updateSubst theta1 theta2 = Map.union theta1 (Map.intersection theta1 theta2)
+-- This is what was in the original code, which behaves slightly differently
+--updateSubst theta1 _ | Map.null theta1 = Map.empty
+--updateSubst theta1 theta2 = Map.unionWith (\ _ term2 -> term2) theta1 theta2
diff --git a/testdata/split-merge-expected/Data/Logic/Satisfiable.hs b/testdata/split-merge-expected/Data/Logic/Satisfiable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Satisfiable.hs
@@ -0,0 +1,53 @@
+-- |Do satisfiability computations on any FirstOrderFormula formula by
+-- converting it to a convenient instance of PropositionalFormula and
+-- using the satisfiable function from that instance.  Currently we
+-- use the satisfiable function from the PropLogic package, by the
+-- Bucephalus project.
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Satisfiable
+    ( satisfiable
+    , theorem
+    , inconsistant
+    , invalid
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), toPropositional)
+import Data.Logic.Classes.Literal.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Instances.PropLogic ()
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set as Set (Set, toList)
+import qualified PropLogic as PL (PropAlg(satisfiable), PropForm(A, CJ, DJ))
+
+-- |Is there any variable assignment that makes the formula true?
+-- satisfiable :: forall formula atom term v f m. (Monad m, FirstOrderFormula formula atom v, Formula atom term v, Term term v f, Ord formula, Literal formula atom v, Ord atom) =>
+--                 formula -> SkolemT v term m Bool
+satisfiable :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                Ord atom, Monad m, Eq formula, Ord formula) =>
+               formula -> SkolemT v term m Bool
+satisfiable f =
+    do (clauses :: Set.Set (Set.Set formula)) <- clauseNormalForm f
+       let f' = PL.CJ . map (PL.DJ . map (toPropositional PL.A)) . map Set.toList . Set.toList $ clauses
+       return . PL.satisfiable $ f'
+
+-- |Is the formula always false?  (Not satisfiable.)
+inconsistant :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                 Ord atom, Monad m, Eq formula, Ord formula) =>
+                formula -> SkolemT v term m Bool
+inconsistant f =  satisfiable f >>= return . not
+
+-- |Is the negation of the formula inconsistant?
+theorem :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+theorem f = inconsistant ((.~.) f)
+
+-- |A formula is invalid if it is neither a theorem nor inconsistent.
+invalid :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+invalid f = inconsistant f >>= \ fi -> theorem f >>= \ ft -> return (not (fi || ft))
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Common.hs b/testdata/split-merge-expected/Data/Logic/Types/Common.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Common.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Types.Common where
+
+import Data.Logic.Classes.Variable (Variable(..))
+import qualified Data.Set as Set
+import Text.PrettyPrint (text)
+
+instance Variable String where
+    variant x vars = if Set.member x vars then variant (x ++ "'") vars else x
+    prefix p x = p ++ x
+    prettyVariable = text
+
+{-
+instance Variable String where
+    variant v vs =
+        if Set.member v vs then variant (next v) (Set.insert v vs) else v
+        where
+          next :: String -> String
+          next s =
+              case break (not . isDigit) (reverse s) of
+                (_, "") -> "x"
+                ("", nondigits) -> nondigits ++ "2"
+                (digits, nondigits) -> nondigits ++ show (1 + read (reverse digits) :: Int)
+-}
diff --git a/testdata/split-merge-expected/Data/Logic/Types/FirstOrder.hs b/testdata/split-merge-expected/Data/Logic/Types/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/FirstOrder.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             GeneralizedNewtypeDeriving, MultiParamTypeClasses, TemplateHaskell, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures -fno-warn-orphans #-}
+-- |Data types which are instances of the Logic type class for use
+-- when you just want to use the classes and you don't have a
+-- particular representation you need to use.
+module Data.Logic.Types.FirstOrder
+    ( Formula(..)
+    , PTerm(..)
+    , Predicate(..)
+    ) where
+
+import Data.Data (Data)
+import qualified Data.Logic.Classes.Apply as C (Predicate)
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import Data.Logic.Classes.Equals (AtomEq(..), prettyAtomEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder, Quant(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (botFixity, HasFixity(..), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function, Term(..))
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.SafeCopy (base, deriveSafeCopy, extension, MigrateFrom(..), SafeCopy)
+import Data.Typeable (Typeable)
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula v p f
+    = Predicate (Predicate p (PTerm v f))
+    | Combine (Combination (Formula v p f))
+    | Quant Quant v (Formula v p f)
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- |A temporary type used in the fold method to represent the
+-- combination of a predicate and its arguments.  This reduces the
+-- number of arguments to foldFirstOrder and makes it easier to manage the
+-- mapping of the different instances to the class methods.
+data Predicate p term
+    = Equal term term
+    | Apply p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | The range of a term is an element of a set.
+data PTerm v f
+    = Var v                         -- ^ A variable, either free or
+                                    -- bound by an enclosing quantifier.
+    | FunApp f [PTerm v f]           -- ^ Function application.
+                                    -- Constants are encoded as
+                                    -- nullary functions.  The result
+                                    -- is another term.
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+instance Negatable (Formula v p f) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants p => Constants (Formula v p f) where
+    fromBool = Predicate . fromBool
+    asBool (Predicate x) = asBool x
+    asBool _ = Nothing
+
+instance Constants p => Constants (Predicate p (PTerm v f)) where
+    fromBool x = Apply (fromBool x) []
+    asBool (Apply p _) = asBool p
+    asBool _ = Nothing
+
+instance (Constants (Formula v p f) {-, Ord v, Ord p, Ord f-}) => Combinable (Formula v p f) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+instance (C.Predicate p, Function f v) => C.Formula (Formula v p f) (Predicate p (PTerm v f)) where
+    atomic = Predicate
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v, Constants (Formula v p f), Combinable (Formula v p f)
+         ) => PropositionalFormula (Formula v p f) (Predicate p (PTerm v f)) where
+    foldPropositional co tf at formula =
+        maybe testFm tf (asBool formula)
+        where
+          testFm =
+              case formula of
+                Quant _ _ _ -> error "foldF0: quantifiers should not be present"
+                Combine x -> co x
+                Predicate x -> at x
+
+instance (Variable v, Function f v) => Term (PTerm v f) v f where
+    foldTerm vf fn t =
+        case t of
+          Var v -> vf v
+          FunApp f ts -> fn f ts
+    zipTerms v f t1 t2 =
+        case (t1, t2) of
+          (Var v1, Var v2) -> v v1 v2
+          (FunApp f1 ts1, FunApp f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Var
+    fApp x args = FunApp x args
+
+{-
+instance (Arity p, Constants p) => Atom (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtom ap (Apply p ts) = ap p ts
+    foldAtom ap (Constant x) = ap (fromBool x) []
+    foldAtom _ _ = error "foldAtom Predicate"
+    zipAtoms ap (Apply p1 ts1) (Apply p2 ts2) = ap p1 ts1 p2 ts2
+    zipAtoms ap (Constant x) (Constant y) = ap (fromBool x) [] (fromBool y) []
+    zipAtoms _ _ _ = error "zipAtoms Predicate"
+    apply' = Apply
+-}
+
+instance C.Predicate p => AtomEq (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtomEq ap tf _ (Apply p ts) = maybe (ap p ts) tf (asBool p)
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    equals = Equal
+    applyEq' = Apply
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          AtomEq (Predicate p (PTerm v f)) p (PTerm v f),
+          Constants (Formula v p f),
+          Variable v, C.Predicate p, Function f v
+         ) => FirstOrderFormula (Formula v p f) (Predicate p (PTerm v f)) v where
+    for_all v x = Quant Forall v x
+    exists v x = Quant Exists v x
+    foldFirstOrder qu co tf at f =
+        maybe testFm tf (asBool f)
+            where testFm = case f of
+                             Quant op v f' -> qu op v f'
+                             Combine x -> co x
+                             Predicate x -> at x
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Quant q1 v1 f1', Quant q2 v2 f2') -> qu q1 v1 (Quant q1 v1 f1') q2 v2 (Quant q2 v2 f2')
+          (Combine x, Combine y) -> co x y
+          (Predicate x, Predicate y) -> at x y
+          _ -> Nothing
+
+instance (Constants (Formula v p f),
+          Variable v, Ord v, Data v, Show v,
+          Arity p, Constants p, Ord p, Data p, Show p,
+          Skolem f, Ord f, Data f, Show f) => Literal (Formula v p f) (Predicate p (PTerm v f)) v where
+    foldLiteral co tf at l =
+        case l of
+          (Combine ((:~:) x)) -> co x
+          (Predicate p) -> at p
+          _ -> error "Literal (Formula v p f)"
+    atomic = Predicate
+-}
+
+instance (Constants p, Ord v, Ord p, Ord f, Constants (Predicate p (PTerm v f)), C.Formula (Formula v p f) (Predicate p (PTerm v f))
+         ) => Literal (Formula v p f) (Predicate p (PTerm v f)) where
+    foldLiteral neg tf at f =
+        case f of
+          Quant _ _ _ -> error "Invalid literal"
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error "Invalid literal"
+          Predicate p -> if p == fromBool True
+                         then tf True
+                         else if p == fromBool False
+                              then tf False
+                              else at p
+
+instance (C.Predicate p, Variable v, Function f v) => C.Atom (Predicate p (PTerm v f)) (PTerm v f) v where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (Apply _ ts) = foldr f r ts
+    foldTerms f r (Equal t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
+
+instance (Variable v, Pretty v,
+          C.Predicate p, Pretty p,
+          Function f v, Pretty f) => Pretty (Predicate p (PTerm v f)) where
+    pretty atom = prettyAtomEq pretty pretty pretty 0 atom
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          C.Predicate p, Variable v, Function f v, HasFixity (Predicate p (PTerm v f))) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v) => Pretty (Formula v p f) where
+    pretty f = prettyFirstOrder (\ _ -> pretty) pretty 0 $ f
+
+instance HasFixity (Predicate p term) where
+    fixity = const botFixity
+
+$(deriveSafeCopy 1 'base ''PTerm)
+$(deriveSafeCopy 1 'base ''Formula)
+$(deriveSafeCopy 2 'extension ''Predicate)
+
+-- Migration --
+
+data Predicate_v1 p term
+    = Equal_v1 term term
+    | NotEqual_v1 term term
+    | Constant_v1 Bool
+    | Apply_v1 p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+$(deriveSafeCopy 1 'base ''Predicate_v1)
+
+instance (SafeCopy p, SafeCopy term) => Migrate (Predicate p term) where
+    type MigrateFrom (Predicate p term) = (Predicate_v1 p term)
+    migrate (Equal_v1 t1 t2) = Equal t1 t2
+    migrate (Apply_v1 p ts) = Apply p ts
+    migrate (NotEqual_v1 _ _) = error "Failure migrating Predicate NotEqual"
+    migrate (Constant_v1 _) = error "Failure migrating Predicate Constant"
diff --git a/testdata/split-merge-expected/Data/Logic/Types/FirstOrderPublic.hs b/testdata/split-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS -Wwarn -fno-warn-orphans #-}
+-- |An instance of FirstOrderFormula which implements Eq and Ord by comparing
+-- after conversion to normal form.  This helps us notice that formula which
+-- only differ in ways that preserve identity, e.g. swapped arguments to a
+-- commutative operator.
+
+module Data.Logic.Types.FirstOrderPublic
+    ( Formula(..)
+    , Bijection(..)
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder)
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm, runNormal)
+import qualified Data.Logic.Types.FirstOrder as N (Formula, Predicate, PTerm)
+import Data.SafeCopy (base, deriveSafeCopy)
+import Data.Set (Set)
+import Data.Typeable (Typeable)
+
+-- |Convert between the public and internal representations.
+class Bijection p i where
+    public :: i -> p
+    intern :: p -> i
+
+-- |The new Formula type is just a wrapper around the Native instance
+-- (which eventually should be renamed the Internal instance.)  No
+-- derived Eq or Ord instances.
+data Formula v p f = Formula {unFormula :: N.Formula v p f} deriving (Data, Typeable, Show)
+
+instance Bijection (Formula v p f) (N.Formula v p f) where
+    public = Formula
+    intern = unFormula
+
+instance Bijection (Combination (Formula v p f)) (Combination (N.Formula v p f)) where
+    public (BinOp x op y) = BinOp (public x) op (public y)
+    public ((:~:) x) = (:~:) (public x)
+    intern (BinOp x op y) = BinOp (intern x) op (intern y)
+    intern ((:~:) x) = (:~:) (intern x)
+
+instance Negatable (Formula v p f) where
+    negatePrivate = Formula . negatePrivate . unFormula
+    foldNegation normal inverted = foldNegation (normal . Formula) (inverted . Formula) . unFormula
+
+instance (Constants (N.Formula v p f), Predicate p, Variable v, Function f v) => Constants (Formula v p f) where
+    fromBool = Formula . fromBool
+    asBool = asBool . unFormula
+
+instance (C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Constants (Formula v p f),
+          Constants (N.Formula v p f),
+          Variable v, Predicate p, Function f v) => Combinable (Formula v p f) where
+    x .<=>. y = Formula $ (unFormula x) .<=>. (unFormula y)
+    x .=>.  y = Formula $ (unFormula x) .=>. (unFormula y)
+    x .|.   y = Formula $ (unFormula x) .|. (unFormula y)
+    x .&.   y = Formula $ (unFormula x) .&. (unFormula y)
+
+instance (Predicate p, Function f v) => C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    atomic = Formula . C.atomic
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v where
+    for_all v x = public $ for_all v (intern x :: N.Formula v p f)
+    exists v x = public $ exists v (intern x :: N.Formula v p f)
+    foldFirstOrder qu co tf at f = foldFirstOrder qu' co' tf at (intern f :: N.Formula v p f)
+        where qu' quant v form = qu quant v (public form)
+              co' x = co (public x)
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Show v, Show p, Show f, HasFixity (Formula v p f), Variable v, Predicate p,
+          Function f v) => PropositionalFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    foldPropositional co tf at f = foldPropositional co' tf at (intern f :: N.Formula v p f)
+        where co' x = co (public x)
+
+-- |Here are the magic Ord and Eq instances
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v) => Ord (Formula v p f) where
+    compare a b =
+        let (a' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula a))
+            (b' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula b)) in
+        case compare a' b' of
+          EQ -> EQ
+          x -> {- if isRenameOf a' b' then EQ else -} x
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v, Constants (N.Predicate p (N.PTerm v f)),
+          FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v) => Eq (Formula v p f) where
+    a == b = compare a b == EQ
+
+instance (Predicate p, Function f v) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Pretty v, Show v, Variable v,
+          Pretty p, Show p, Predicate p,
+          Pretty f, Show f, Function f v) => Pretty (Formula v p f) where
+    pretty formula = prettyFirstOrder (\ _prec a -> pretty a) pretty 0 formula
+
+$(deriveSafeCopy 1 'base ''Formula)
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Harrison/Equal.hs b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Equal.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Types.Harrison.Equal where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(fromBool))
+import Data.Logic.Classes.Equals (AtomEq(..), showFirstOrderFormulaEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (fixityFirstOrder)
+import qualified Data.Logic.Classes.Formula as C (Formula)
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.Logic.Types.Harrison.FOL (TermType(..))
+import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import Data.String (IsString(..))
+import Text.PrettyPrint (cat, text)
+
+data FOLEQ = EQUALS TermType TermType | R String [TermType] deriving (Eq, Ord, Show)
+data PredName = (:=:) | Named String deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Arity PredName where
+    arity (:=:) = Just 2
+    arity _ = Nothing
+
+instance Show (Formula FOLEQ) where
+    show = showFirstOrderFormulaEq
+
+instance HasFixity FOLEQ where
+    fixity (EQUALS _ _) = Fixity 5 InfixL
+    fixity _ = Fixity 10 InfixN
+
+instance IsString PredName where
+    fromString "=" = (:=:)
+    fromString s = Named s
+
+instance Constants PredName where
+    fromBool True = Named "true"
+    fromBool False = Named "false"
+    asBool x
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOLEQ where
+    fromBool x = R (fromBool x) []
+    asBool (R p _)
+        | fromBool True == p = Just True
+        | fromBool False == p = Just False
+        | True = Nothing
+    asBool _ = Nothing
+
+instance Predicate PredName
+
+instance Pretty PredName where
+    pretty (:=:) = text "="
+    pretty (Named s) = text s
+
+-- | Using PredName for the predicate type is not quite appropriate
+-- here, but we need to implement this instance so we can use it as a
+-- superclass of AtomEq below.
+instance Apply FOLEQ PredName TermType where
+    foldApply f _ (EQUALS t1 t2) = f (:=:) [t1, t2]
+    foldApply f tf (R p ts) = maybe (f (Named p) ts) tf (asBool (Named p))
+    apply' (Named p) ts = R p ts
+    apply' (:=:) [t1, t2] = EQUALS t1 t2
+    apply' (:=:) _ = error "arity"
+
+{-
+instance FirstOrderFormula (Formula FOLEQ) FOLEQ String where
+    exists = Exists
+    for_all = Forall
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+    atomic = Atom
+-}
+
+instance C.Formula (Formula FOLEQ) FOLEQ => P.PropositionalFormula (Formula FOLEQ) FOLEQ where
+    foldPropositional co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall _ _ -> error "quantifier in propositional formula"
+          Exists _ _ -> error "quantifier in propositional formula"
+
+instance Pretty FOLEQ where
+    pretty (EQUALS a b) = cat [pretty a, pretty (:=:), pretty b]
+    pretty (R s ts) = cat ([pretty s, pretty "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance HasFixity (Formula FOLEQ) where
+    fixity = fixityFirstOrder
+
+instance C.Formula (Formula FOLEQ) FOLEQ => Literal (Formula FOLEQ) FOLEQ where
+    foldLiteral neg tf at lit =
+        case lit of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> neg fm'
+          _ -> error "Literal (Formula FOLEQ)"
+
+-- instance PredicateEq PredName where
+--     eqp = (:=:)
+
+instance AtomEq FOLEQ PredName TermType where
+    foldAtomEq pr tf _ (R p ts) = maybe (pr (Named p) ts) tf (asBool (Named p))
+    foldAtomEq _ _ eq (EQUALS t1 t2) = eq t1 t2
+    equals = EQUALS
+    applyEq' (Named s) ts = R s ts
+    applyEq' (:=:) [t1, t2] = EQUALS t1 t2
+    applyEq' _ _ = error "arity"
+
+instance C.Atom FOLEQ TermType String where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (R _ ts) = foldr f r ts
+    foldTerms f r (EQUALS t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Harrison/FOL.hs b/testdata/split-merge-expected/Data/Logic/Types/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Harrison/FOL.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Data.Logic.Types.Harrison.FOL
+    ( TermType(..)
+    , FOL(..)
+    , Function(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+--import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool)
+--import Data.Logic.Classes.FirstOrder (foldAtomsFirstOrder, mapAtomsFirstOrder)
+--import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Term(vt, foldTerm, fApp))
+import qualified Data.Logic.Classes.Term as C
+--import qualified Data.Logic.Classes.FirstOrder as C
+--import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import qualified Data.Logic.Types.Common ({- instance Variable String -})
+import Prelude hiding (pred)
+import Text.PrettyPrint (text, cat)
+
+-- -------------------------------------------------------------------------
+-- Terms.                                                                   
+-- -------------------------------------------------------------------------
+
+data TermType
+    = Var String
+    | Fn Function [TermType]
+    deriving (Eq, Ord)
+
+data FOL = R String [TermType] deriving (Eq, Ord, Show)
+
+instance Show TermType where
+    show (Var v) = "vt " ++ show v
+    show (Fn f ts) = "fApp " ++ show f ++ " " ++ show ts
+
+instance Pretty TermType where
+    pretty (Var v) = pretty v
+    pretty (Fn f ts) = cat ([pretty f, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Apply FOL String TermType where
+    foldApply f tf (R p ts) = maybe (f p ts) tf (asBool p)
+    apply' = R
+
+-- | This is probably dangerous.
+instance Constants String where
+    fromBool True = "true"
+    fromBool False = "false"
+    asBool x 
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOL where
+    fromBool x = R (fromBool x) []
+    asBool (R p _) = asBool p
+
+instance Predicate String
+
+{-
+instance Pretty String where
+    pretty = text
+
+instance FirstOrderFormula (Formula FOL) FOL String where
+    -- type C.Term (Formula FOL) = Term
+    -- type V (Formula FOL) = String
+    -- type Pr (Formula FOL) = String
+    -- type Fn (Formula FOL) = String -- ^ Atomic function type
+
+    -- quant C.Exists v fm = H.Exists v fm
+    -- quant C.Forall v fm = H.Forall v fm
+    for_all = H.Forall
+    exists = H.Exists
+    atomic = Atom
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          H.Forall v fm' -> qu C.Forall v fm'
+          H.Exists v fm' -> qu C.Exists v fm'
+-}
+
+instance Pretty FOL where
+    pretty (R p ts) = cat ([pretty p, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Arity String where
+    arity _ = Nothing
+
+-- | The Harrison book uses String for atomic function, but we need
+-- something a little more type safe because of our Skolem class.
+data Function
+    = FName String
+    | Skolem String
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+instance Pretty Function where
+    pretty (FName s) = text s
+    pretty (Skolem v) = text ("sK" ++ v)
+
+instance C.Function Function String
+
+instance Skolem Function String where
+    toSkolem = Skolem
+    isSkolem (Skolem _) = True
+    isSkolem _ = False
+
+instance Term TermType String Function where
+    -- type V Term = String
+    -- type Fn Term = String
+    vt = Var
+    fApp = Fn
+    foldTerm vfn _ (Var x) = vfn x
+    foldTerm _ ffn (Fn f ts) = ffn f ts
+    zipTerms = undefined
+
+instance HasFixity FOL where
+    fixity = const (Fixity 10 InfixN)
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.FirstOrder
+    ( Formula(..)
+    ) where
+
+--import Data.Char (isDigit)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder)
+import qualified Data.Logic.Classes.FirstOrder as C (Quant(Exists, Forall))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity, Pretty(pretty))
+import Data.Logic.Types.Common ()
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    | Forall String (Formula a)
+    | Exists String (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Constants a, Pretty a, HasFixity a) => C.Formula (Formula a) a where
+    atomic = Atom
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula a) a, Constants a, Pretty a, HasFixity a) => FirstOrderFormula (Formula a) a String where
+    for_all = Forall
+    exists = Exists
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+
+instance (FirstOrderFormula (Formula a) a String) => Pretty (Formula a) where
+    pretty = prettyFirstOrder (const pretty) pretty 0
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.Propositional
+    ( Formula(..)
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (Formula atom), Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    -- The atom type for this formula is the same as its first type parameter.
+    foldPropositional co tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> co ((:~:) f)
+          And f g -> co (BinOp f (:&:) g)
+          Or f g -> co (BinOp f (:|:) g)
+          Imp f g -> co (BinOp f (:=>:) g)
+          Iff f g -> co (BinOp f (:<=>:) g)
+          Atom x -> at x
+
+instance (HasFixity atom, Pretty atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> neg f
+          Atom x -> at x
+          _ -> error ("Unexpected literal " ++ show (pretty formula))
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Harrison/Prop.hs b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Harrison/Prop.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Prop
+    ( Prop(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Pretty
+import Data.Logic.Classes.Propositional (showPropositional)
+import Data.Logic.Types.Harrison.Formulas.Propositional (Formula(..))
+import Prelude hiding (negate)
+import Text.PrettyPrint (text)
+
+-- =========================================================================
+-- Basic stuff for propositional logic: datatype, parsing and printing.     
+-- =========================================================================
+
+newtype Prop = P {pname :: String} deriving (Read, Data, Typeable, Eq, Ord)
+
+instance Show Prop where
+    show x = "P " ++ show (pname x)
+
+instance Pretty Prop where
+    pretty = text . pname
+
+instance HasFixity String where
+    fixity = const botFixity
+
+instance HasFixity Prop where
+    fixity = const botFixity
+
+instance Show (Formula Prop) where
+    show = showPropositional show
+
+instance Show (Formula String) where
+    show = showPropositional show
diff --git a/testdata/split-merge-expected/Data/Logic/Types/Propositional.hs b/testdata/split-merge-expected/Data/Logic/Types/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-expected/Data/Logic/Types/Propositional.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+module Data.Logic.Types.Propositional where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula atom
+    = Combine (Combination (Formula atom))
+    | Atom atom
+    | T
+    | F
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq,Ord,Data,Typeable)
+
+instance Negatable (Formula atom) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Ord atom) => Combinable (Formula atom) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+
+instance Constants (Formula atom) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error ("Unexpected literal: " ++ show (pretty formula))
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (C.Formula (Formula atom) atom, Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    foldPropositional co tf at formula =
+        case formula of
+          Combine x -> co x
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split-merge-merge-expected/Data/Boolean.hs b/testdata/split-merge-merge-expected/Data/Boolean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Boolean.hs
@@ -0,0 +1,157 @@
+{-# OPTIONS -fno-warn-incomplete-patterns #-}
+-- |
+-- Module      : Data.Boolean
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This library provides a representation of boolean formulas that is
+-- used by the solver in "Data.Boolean.SatSolver".
+-- 
+-- We also define a function to simplify formulas, a type for
+-- conjunctive normalforms, and a function that creates them from
+-- boolean formulas.
+-- 
+module Data.Boolean ( 
+
+  Boolean(..), 
+
+  Literal(..), literalVar, invLiteral, isPositiveLiteral, 
+
+  CNF, Clause, booleanToCNF
+
+  ) where
+
+import Data.Maybe ( mapMaybe )
+import qualified Data.IntMap as IM
+
+import Control.Monad ( guard, liftM )
+
+-- | Boolean formulas are represented as values of type @Boolean@.
+-- 
+data Boolean
+  -- | Variables are labeled with an @Int@,
+  = Var Int
+  -- | @Yes@ represents /true/,
+  | Yes
+  -- | @No@ represents /false/,
+  | No
+  -- | @Not@ constructs negated formulas,
+  | Not Boolean
+  -- | and finally we provide conjunction
+  | Boolean :&&: Boolean
+  -- | and disjunction of boolean formulas.
+  | Boolean :||: Boolean
+ deriving Show
+
+-- | Literals are variables that occur either positively or negatively.
+-- 
+data Literal = Pos Int | Neg Int deriving (Eq, Show)
+
+-- | This function returns the name of the variable in a literal.
+-- 
+literalVar :: Literal -> Int
+literalVar (Pos n) = n
+literalVar (Neg n) = n
+
+-- | This function negates a literal.
+-- 
+invLiteral :: Literal -> Literal
+invLiteral (Pos n) = Neg n
+invLiteral (Neg n) = Pos n
+
+-- | This predicate checks whether the given literal is positive.
+-- 
+isPositiveLiteral :: Literal -> Bool
+isPositiveLiteral (Pos _) = True
+isPositiveLiteral _       = False
+
+-- | Conjunctive normalforms are lists of lists of literals.
+-- 
+type CNF     = [Clause]
+type Clause  = [Literal]
+
+-- | 
+-- We convert boolean formulas to conjunctive normal form by pushing
+-- negations down to variables and repeatedly applying the
+-- distributive laws.
+-- 
+booleanToCNF :: Boolean -> CNF
+booleanToCNF
+  = mapMaybe (simpleClause . map literal . disjunction)
+  . conjunction
+  . asLongAsPossible distribute
+  . asLongAsPossible pushNots
+  . asLongAsPossible elim
+ where
+  elim (Not Yes)      = Just No
+  elim (Not No)       = Just Yes
+  elim (No  :&&: _)   = Just No
+  elim (Yes :&&: x)   = Just x
+  elim (_   :&&: No)  = Just No
+  elim (x   :&&: Yes) = Just x 
+  elim (Yes :||: _)   = Just Yes
+  elim (No  :||: x)   = Just x
+  elim (_   :||: Yes) = Just Yes
+  elim (x   :||: No)  = Just x
+  elim _              = Nothing
+
+  pushNots (Not (Not x))  = Just x
+  pushNots (Not (x:&&:y)) = Just (Not x :||: Not y)
+  pushNots (Not (x:||:y)) = Just (Not x :&&: Not y)
+  pushNots _              = Nothing
+
+  distribute (x:||:(y:&&:z)) = Just ((x:||:y):&&:(x:||:z))
+  distribute ((x:&&:y):||:z) = Just ((x:||:z):&&:(y:||:z))
+  distribute _               = Nothing
+
+  literal (Var x)       = Pos x
+  literal (Not (Var x)) = Neg x
+
+
+-- private helper functions
+
+-- remove duplicate literals from clauses and drop clauses that
+-- contain one literal both positively and negatively.
+--
+simpleClause :: Clause -> Maybe Clause
+simpleClause = liftM (map lit . IM.toList) . foldl add (Just IM.empty)
+ where
+  lit (x,True)  = Pos x
+  lit (x,False) = Neg x
+
+  add mm l = do
+    m <- mm
+    let x = literalVar l; kind = isPositiveLiteral l
+    maybe (Just (IM.insert x kind m))
+          (\b -> guard (b==kind) >> Just m)
+          (IM.lookup x m)
+
+conjunction :: Boolean -> [Boolean]
+conjunction b = flat b []
+ where flat Yes      = id
+       flat (x:&&:y) = flat x . flat y
+       flat x        = (x:)
+
+disjunction :: Boolean -> [Boolean]
+disjunction b = flat b []
+ where flat No       = id
+       flat (x:||:y) = flat x . flat y
+       flat x        = (x:)
+
+asLongAsPossible :: (Boolean -> Maybe Boolean) -> Boolean -> Boolean
+asLongAsPossible f = everywhere g
+ where g x = maybe x (everywhere g) (f x)
+
+everywhere :: (Boolean -> Boolean) -> Boolean -> Boolean
+everywhere f = f . atChildren (everywhere f)
+
+atChildren :: (Boolean -> Boolean) -> Boolean -> Boolean
+atChildren f (Not x)  = Not (f x)
+atChildren f (x:&&:y) = f x :&&: f y
+atChildren f (x:||:y) = f x :||: f y
+atChildren _ x        = x
+
diff --git a/testdata/split-merge-merge-expected/Data/Boolean/SatSolver.hs b/testdata/split-merge-merge-expected/Data/Boolean/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Boolean/SatSolver.hs
@@ -0,0 +1,183 @@
+-- |
+-- Module      : Data.Boolean.SatSolver
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- 
+-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- This Haskell library provides an implementation of the
+-- Davis-Putnam-Logemann-Loveland algorithm
+-- (cf. <http://en.wikipedia.org/wiki/DPLL_algorithm>) for the boolean
+-- satisfiability problem. It not only allows to solve boolean
+-- formulas in one go but also to add constraints and query bindings
+-- of variables incrementally.
+-- 
+-- The implementation is not sophisticated at all but uses the basic
+-- DPLL algorithm with unit propagation.
+-- 
+module Data.Boolean.SatSolver (
+
+  Boolean(..), SatSolver, Literal(..), literalVar, invLiteral, isPositiveLiteral, CNF, Clause, booleanToCNF,
+
+  newSatSolver, isSolved, 
+
+  lookupVar, assertTrue, assertTrue', branchOnVar, selectBranchVar, solve, isSolvable
+
+  ) where
+
+import Data.List
+import Data.Boolean
+
+import Control.Monad.Writer
+
+import qualified Data.IntMap as IM
+
+-- | A @SatSolver@ can be used to solve boolean formulas.
+-- 
+data SatSolver = SatSolver { clauses :: CNF, bindings :: IM.IntMap Bool }
+ deriving Show
+
+-- | A new SAT solver without stored constraints.
+-- 
+newSatSolver :: SatSolver
+newSatSolver = SatSolver [] IM.empty
+
+-- | This predicate tells whether all constraints are solved.
+-- 
+isSolved :: SatSolver -> Bool
+isSolved = null . clauses
+
+-- |
+-- We can lookup the binding of a variable according to the currently
+-- stored constraints. If the variable is unbound, the result is
+-- @Nothing@.
+-- 
+lookupVar :: Int -> SatSolver -> Maybe Bool
+lookupVar name = IM.lookup name . bindings
+
+-- | 
+-- We can assert boolean formulas to update a @SatSolver@. The
+-- assertion may fail if the resulting constraints are unsatisfiable.
+-- 
+assertTrue :: MonadPlus m => Boolean -> SatSolver -> m SatSolver
+assertTrue formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      (booleanToCNF formula)
+  simplify (solver { clauses = newClauses })
+
+assertTrue' :: MonadPlus m => CNF -> SatSolver -> m SatSolver
+assertTrue' formula solver = do
+  newClauses <- foldl (addClause (bindings solver))
+                      (return (clauses solver))
+                      formula
+  simplify (solver { clauses = newClauses })
+
+-- |
+-- This function guesses a value for the given variable, if it is
+-- currently unbound. As this is a non-deterministic operation, the
+-- resulting solvers are returned in an instance of @MonadPlus@.
+-- 
+branchOnVar :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnVar name solver =
+  maybe (branchOnUnbound name solver)
+        (const (return solver))
+        (lookupVar name solver)
+
+-- |
+-- We select a variable from the shortest clause hoping to produce a
+-- unit clause.
+--
+selectBranchVar :: SatSolver -> Int
+selectBranchVar = literalVar . head . head . sortBy shorter . clauses
+
+-- | 
+-- This function guesses values for variables such that the stored
+-- constraints are satisfied. The result may be non-deterministic and
+-- is, hence, returned in an instance of @MonadPlus@.
+-- 
+solve :: MonadPlus m => SatSolver -> m SatSolver
+solve solver
+  | isSolved solver = return solver
+  | otherwise = branchOnUnbound (selectBranchVar solver) solver >>= solve
+
+-- |
+-- This predicate tells whether the stored constraints are
+-- solvable. Use with care! This might be an inefficient operation. It
+-- tries to find a solution using backtracking and returns @True@ if
+-- and only if that fails.
+-- 
+isSolvable :: SatSolver -> Bool
+isSolvable = not . null . solve
+
+
+-- private helper functions
+
+addClause :: MonadPlus m => IM.IntMap Bool -> m [Clause] -> Clause -> m [Clause]
+addClause binds mclauses newClause = do
+  oldClauses <- mclauses
+  let unboundLits = foldl (addUnbound binds) (Just []) newClause
+  maybe (return oldClauses)
+        (\lits -> guard (not (null lits)) >> return (lits:oldClauses))
+        unboundLits
+
+addUnbound :: IM.IntMap Bool -> Maybe Clause -> Literal -> Maybe Clause
+addUnbound binds mlits lit = do
+  lits <- mlits
+  maybe (Just (lit:lits))
+        (\b -> guard (b /= isPositiveLiteral lit) >> return lits)
+        (IM.lookup (literalVar lit) binds)
+
+updateSolver :: MonadPlus m => CNF -> [(Int,Bool)] -> SatSolver -> m SatSolver
+updateSolver cs bs solver = do
+  bs' <- foldr (uncurry insertBinding) (return (bindings solver)) bs
+  return $ solver { clauses = cs, bindings = bs' }
+
+insertBinding :: MonadPlus m
+              => Int -> Bool -> m (IM.IntMap Bool) -> m (IM.IntMap Bool)
+insertBinding name newValue binds = do
+  bs <- binds
+  maybe (return (IM.insert name newValue bs))
+        (\oldValue -> do guard (oldValue==newValue); return bs)
+        (IM.lookup name bs)
+
+simplify :: MonadPlus m => SatSolver -> m SatSolver
+simplify solver = do
+  (cs,bs) <- runWriterT . simplifyClauses . clauses $ solver
+  updateSolver cs bs solver
+
+simplifyClauses :: MonadPlus m => CNF -> WriterT [(Int,Bool)] m CNF
+simplifyClauses [] = return []
+simplifyClauses allClauses = do
+  let shortestClause = head . sortBy shorter $ allClauses
+  guard (not (null shortestClause))
+  if null (tail shortestClause)
+   then propagate (head shortestClause) allClauses >>= simplifyClauses
+   else return allClauses
+
+propagate :: MonadPlus m => Literal -> CNF -> WriterT [(Int,Bool)] m CNF
+propagate literal allClauses = do
+  tell [(literalVar literal, isPositiveLiteral literal)]
+  return (foldr prop [] allClauses)
+ where
+  prop c cs | literal `elem` c = cs
+            | otherwise        = filter (invLiteral literal/=) c : cs
+
+branchOnUnbound :: MonadPlus m => Int -> SatSolver -> m SatSolver
+branchOnUnbound name solver =
+  guess (Pos name) solver `mplus` guess (Neg name) solver
+
+guess :: MonadPlus m => Literal -> SatSolver -> m SatSolver
+guess literal solver = do
+  (cs,bs) <- runWriterT (propagate literal (clauses solver) >>= simplifyClauses)
+  updateSolver cs bs solver
+
+shorter :: [a] -> [a] -> Ordering
+shorter []     []     = EQ
+shorter []     _      = LT
+shorter _      []     = GT
+shorter (_:xs) (_:ys) = shorter xs ys
+
+
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Apply.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Apply.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Apply.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures #-}
+-- | The Apply class represents a type of atom the only supports predicate application.
+module Data.Logic.Classes.Apply
+    ( Apply(..)
+    , Predicate
+    , apply
+    , zipApplys
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , showApply
+    , prettyApply
+    , varApply
+    , substApply
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Constants
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Term (Term, showTerm, prettyTerm, fvt, tsubst)
+import Data.List (intercalate, intersperse)
+import Data.Maybe (fromMaybe)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), text, empty, parens, cat)
+
+class (Arity p, Constants p, Eq p, Ord p, Data p, Pretty p) => Predicate p
+
+class Predicate p => Apply atom p term | atom -> p term where
+    foldApply :: (p -> [term] -> r) -> (Bool -> r) -> atom -> r
+    apply' :: p -> [term] -> atom
+
+-- | apply' with an arity check - clients should always call this.
+apply :: Apply atom p term => p -> [term] -> atom
+apply p ts =
+    case arity p of
+      Just n | n /= length ts -> error "arity"
+      _ -> apply' p ts
+
+zipApplys :: Apply atom p term =>
+            (p -> [term] -> p -> [term] -> Maybe r)
+         -> (Bool -> Bool -> Maybe r)
+         -> atom -> atom -> Maybe r
+zipApplys ap tf a1 a2 =
+    foldApply ap' tf' a1
+    where
+      ap' p1 ts1 = foldApply (ap p1 ts1) (\ _ -> Nothing) a2
+      tf' x1 = foldApply (\ _ _ -> Nothing) (tf x1) a2
+
+apply0 p = if fromMaybe 0 (arity p) == 0 then apply' p [] else error "arity"
+apply1 p a = if fromMaybe 1 (arity p) == 1 then apply' p [a] else error "arity"
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then apply' p [a,b] else error "arity"
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then apply' p [a,b,c] else error "arity"
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then apply' p [a,b,c,d] else error "arity"
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then apply' p [a,b,c,d,e] else error "arity"
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then apply' p [a,b,c,d,e,f] else error "arity"
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then apply' p [a,b,c,d,e,f,g] else error "arity"
+
+showApply :: (Apply atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showApply =
+    foldApply (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+              (\ x -> if x then "true" else "false")
+
+prettyApply :: (Apply atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyApply pv pp pf prec atom =
+    foldApply (\ p ts ->
+                   pp p <> case ts of
+                             [] -> empty
+                             _ -> parens (cat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+              (\ x -> text (if x then "true" else "false"))
+              atom
+
+-- | Return the variables that occur in an instance of Apply.
+varApply :: (Apply atom p term, Term term v f) => atom -> Set.Set v
+varApply = foldApply (\ _ args -> Set.unions (map fvt args)) (const Set.empty)
+
+substApply :: (Apply atom p term, Constants atom, Term term v f) => Map.Map v term -> atom -> atom
+substApply env = foldApply (\ p args -> apply p (map (tsubst env) args)) fromBool
+
+{-
+instance (Apply atom p term, Term term v f, Constants atom) => Formula atom term v where
+    allVariables = varApply
+    freeVariables = varApply
+    substitute = substApply
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Arity.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Arity.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Arity.hs
@@ -0,0 +1,11 @@
+module Data.Logic.Classes.Arity
+    ( Arity(arity)
+    ) where
+
+-- |A class that characterizes how many arguments a predicate or
+-- function takes.  Depending on the context, a result of Nothing may
+-- mean that the arity is undetermined or unknown.  However, even if
+-- this returns Nothing, the same number of arguments must be passed
+-- to all uses of a given predicate or function.
+class Arity p where
+    arity :: p -> Maybe Int
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Atom.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Atom.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Atom.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, TypeFamilies #-}
+-- | Substitution and finding variables are two basic operations on
+-- formulas that contain terms and variables.  If a formula type
+-- supports quantifiers we can also find free variables, otherwise all
+-- variables are considered free.
+module Data.Logic.Classes.Atom
+    ( Atom(..)
+    -- , Formula(..)
+    ) where
+
+import Control.Applicative.Error (Failing)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+{-
+class Formula formula term v | formula -> term v where
+    substitute :: Map.Map v term -> formula -> formula
+    allVariables :: formula -> Set.Set v
+    freeVariables :: formula -> Set.Set v
+    unify :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    match :: Map.Map v term -> formula -> formula -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> formula -> r
+    isRename :: formula -> formula -> Bool
+    getSubst :: Map.Map v term -> formula -> Map.Map v term
+-}
+
+class Atom atom term v | atom -> term v where
+    substitute :: Map.Map v term -> atom -> atom
+    allVariables :: atom -> Set.Set v
+    freeVariables :: atom -> Set.Set v
+    unify :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    match :: Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+    -- ^ Very similar to unify, not quite sure if there is a difference
+    foldTerms :: (term -> r -> r) -> r -> atom -> r
+    isRename :: atom -> atom -> Bool
+    getSubst :: Map.Map v term -> atom -> Map.Map v term
+
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/ClauseNormalForm.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.ClauseNormalForm
+    ( ClauseNormalFormula(clauses, makeCNF, satisfiable)
+    ) where
+
+import Control.Monad (MonadPlus)
+import Data.Logic.Classes.Negate
+import Data.Set as S
+
+-- |A class to represent formulas in CNF, which is the conjunction of
+-- a set of disjuncted literals each which may or may not be negated.
+class (Negatable lit, Eq lit, Ord lit) => ClauseNormalFormula cnf lit | cnf -> lit where
+    clauses :: cnf -> S.Set (S.Set lit)
+    makeCNF :: S.Set (S.Set lit) -> cnf
+    satisfiable :: MonadPlus m => cnf -> m Bool
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Combine.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Combine.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Combine.hs
@@ -0,0 +1,123 @@
+-- | Class Logic defines the basic boolean logic operations,
+-- AND, OR, NOT, and so on.  Definitions which pertain to both
+-- propositional and first order logic are here.
+{-# LANGUAGE DeriveDataTypeable #-}
+module Data.Logic.Classes.Combine
+    ( Combinable(..)
+    , Combination(..)
+    , combine
+    , BinOp(..)
+    , binop
+    -- * Unicode aliases for Combinable class methods
+    , (∧)
+    , (∨)
+    , (⇒)
+    , (⇔)
+    -- * Use in Harrison's code
+    , (==>)
+    , (<=>)
+    , prettyBinOp
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Negate (Negatable, (.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- | A type class for logical formulas.  Minimal implementation:
+-- @
+--  (.|.)
+-- @
+class (Negatable formula) => Combinable formula where
+    -- | Disjunction/OR
+    (.|.) :: formula -> formula -> formula
+
+    -- | Derived formula combinators.  These could (and should!) be
+    -- overridden with expressions native to the instance.
+    --
+    -- | Conjunction/AND
+    (.&.) :: formula -> formula -> formula
+    x .&. y = (.~.) ((.~.) x .|. (.~.) y)
+    -- | Formula combinators: Equivalence
+    (.<=>.) :: formula -> formula -> formula
+    x .<=>. y = (x .=>. y) .&. (y .=>. x)
+    -- | Implication
+    (.=>.) :: formula -> formula -> formula
+    x .=>. y = ((.~.) x .|. y)
+    -- | Reverse implication:
+    (.<=.) :: formula -> formula -> formula
+    x .<=. y = y .=>. x
+    -- | Exclusive or
+    (.<~>.) :: formula -> formula -> formula
+    x .<~>. y = ((.~.) x .&. y) .|. (x .&. (.~.) y)
+    -- | Nor
+    (.~|.) :: formula -> formula -> formula
+    x .~|. y = (.~.) (x .|. y)
+    -- | Nand
+    (.~&.) :: formula -> formula -> formula
+    x .~&. y = (.~.) (x .&. y)
+
+infixl 1  .<=>. ,  .<~>., ⇔, <=>
+infixr 2  .=>., ⇒, ==>
+infixr 3  .|., ∨
+infixl 4  .&., ∧
+
+-- |'Combination' is a helper type used in the signatures of the
+-- 'foldPropositional' and 'foldFirstOrder' methods so can represent
+-- all the ways that formulas can be combined using boolean logic -
+-- negation, logical And, and so forth.
+data Combination formula
+    = BinOp formula BinOp formula
+    | (:~:) formula
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | A helper function for building folds:
+-- @
+--   foldPropositional combine atomic
+-- @
+-- is a no-op.
+combine :: Combinable formula => Combination formula -> formula
+combine (BinOp f1 (:<=>:) f2) = f1 .<=>. f2
+combine (BinOp f1 (:=>:) f2) = f1 .=>. f2
+combine (BinOp f1 (:&:) f2) = f1 .&. f2
+combine (BinOp f1 (:|:) f2) = f1 .|. f2
+combine ((:~:) f) = (.~.) f
+
+-- | Represents the boolean logic binary operations, used in the
+-- Combination type above.
+data BinOp
+    = (:<=>:)  -- ^ Equivalence
+    |  (:=>:)  -- ^ Implication
+    |  (:&:)  -- ^ AND
+    |  (:|:)  -- ^ OR
+    deriving (Eq, Ord, Data, Typeable, Enum, Bounded, Show, Read)
+
+binop :: Combinable formula => formula -> BinOp -> formula -> formula
+binop a (:&:) b = a .&. b
+binop a (:|:) b = a .|. b
+binop a (:=>:) b = a .=>. b
+binop a (:<=>:) b = a .<=>. b
+
+(∧) :: Combinable formula => formula -> formula -> formula
+(∧) = (.&.)
+(∨) :: Combinable formula => formula -> formula -> formula
+(∨) = (.|.)
+-- | ⇒ can't be a function when -XUnicodeSyntax is enabled.
+(⇒) :: Combinable formula => formula -> formula -> formula
+(⇒) = (.=>.)
+(⇔) :: Combinable formula => formula -> formula -> formula
+(⇔) = (.<=>.)
+
+(==>) :: Combinable formula => formula -> formula -> formula
+(==>) = (.=>.)
+(<=>) :: Combinable formula => formula -> formula -> formula
+(<=>) = (.<=>.)
+
+prettyBinOp :: BinOp -> Doc
+prettyBinOp (:<=>:) = text "⇔"
+prettyBinOp (:=>:) = text "⇒"
+prettyBinOp (:&:) = text "∧"
+prettyBinOp (:|:) = text "∨"
+
+instance Pretty BinOp where
+    pretty = prettyBinOp
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Constants.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Constants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Constants.hs
@@ -0,0 +1,40 @@
+module Data.Logic.Classes.Constants
+    ( Constants(asBool, fromBool)
+    , ifElse
+    , true
+    , (⊨)
+    , false
+    , (⊭)
+    , prettyBool
+    ) where
+
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Text.PrettyPrint (Doc, text)
+
+-- |Some types in the Logic class heirarchy need to have True and
+-- False elements.
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+true :: Constants p => p
+true = fromBool True
+
+false :: Constants p => p
+false = fromBool False
+
+ifElse :: a -> a -> Bool -> a
+ifElse t _ True = t
+ifElse _ f False = f
+
+(⊨) :: Constants formula => formula
+(⊨) = true
+(⊭) :: Constants formula => formula
+(⊭) = false
+
+prettyBool :: Bool -> Doc
+prettyBool True = text "⊨"
+prettyBool False = text "⊭"
+
+instance Pretty Bool where
+    pretty = prettyBool
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Equals.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Equals.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Equals.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+-- | Support for equality.
+module Data.Logic.Classes.Equals
+    ( AtomEq(..)
+    , applyEq
+    , PredicateName(..)
+    , zipAtomsEq
+    , apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7
+    , pApp, pApp0, pApp1, pApp2, pApp3, pApp4, pApp5, pApp6, pApp7
+    , showFirstOrderFormulaEq
+    , (.=.), (≡)
+    , (.!=.), (≢)
+    , fromAtomEq
+    , showAtomEq
+    , prettyAtomEq
+    , varAtomEq
+    , substAtomEq
+    , funcsAtomEq
+    ) where
+
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), Quant(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty))
+import Data.Logic.Classes.Term (convertTerm, funcs, fvt, prettyTerm, showTerm, Term, tsubst)
+import qualified Data.Map as Map (Map)
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set (empty, Set, union, unions)
+import Text.PrettyPrint ((<+>), (<>), Doc, empty, hcat, nest, parens, text)
+
+-- | Its not safe to make Atom a superclass of AtomEq, because the Atom methods will fail on AtomEq instances.
+class Predicate p => AtomEq atom p term | atom -> p term, term -> atom p where
+    foldAtomEq :: (p -> [term] -> r) -> (Bool -> r) -> (term -> term -> r) -> atom -> r
+    equals :: term -> term -> atom
+    applyEq' :: p -> [term] -> atom
+
+-- | applyEq' with an arity check - clients should always call this.
+applyEq :: AtomEq atom p term => p -> [term] -> atom
+applyEq p ts =
+    case arity p of
+      Just n | n /= length ts -> arityError p ts
+      _ -> applyEq' p ts
+
+-- | A way to represent any predicate's name.  Frequently the equality
+-- predicate has no standalone representation in the p type, it is
+-- just a constructor in the atom type, or even the formula type.
+data PredicateName p = Named p Int | Equals deriving (Eq, Ord, Show)
+
+instance (Pretty p, Ord p) => Pretty (PredicateName p) where
+    pretty Equals = text "="
+    pretty (Named p _) = pretty p
+
+zipAtomsEq :: AtomEq atom p term =>
+              (p -> [term] -> p -> [term] -> Maybe r)
+           -> (Bool -> Bool -> Maybe r)
+           -> (term -> term -> term -> term -> Maybe r)
+           -> atom -> atom -> Maybe r
+zipAtomsEq ap tf eq a1 a2 =
+    foldAtomEq ap' tf' eq' a1
+    where
+      ap' p1 ts1 = foldAtomEq (ap p1 ts1) (\ _ -> Nothing) (\ _ _ -> Nothing) a2
+      tf' x1 = foldAtomEq (\ _ _ -> Nothing) (tf x1) (\ _ _ -> Nothing) a2
+      eq' t1 t2 = foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) (eq t1 t2) a2
+
+apply0 :: AtomEq atom p term => p -> atom
+apply0 p = if fromMaybe 0 (arity p) == 0 then applyEq' p [] else arityError p []
+apply1 :: AtomEq atom p a => p -> a -> atom
+apply1 p a = if fromMaybe 1 (arity p) == 1 then applyEq' p [a] else arityError p [a]
+apply2 :: AtomEq atom p a => p -> a -> a -> atom
+apply2 p a b = if fromMaybe 2 (arity p) == 2 then applyEq' p [a,b] else arityError p [a,b]
+apply3 :: AtomEq atom p a => p -> a -> a -> a -> atom
+apply3 p a b c = if fromMaybe 3 (arity p) == 3 then applyEq' p [a,b,c] else arityError p [a,b,c]
+apply4 :: AtomEq atom p a => p -> a -> a -> a -> a -> atom
+apply4 p a b c d = if fromMaybe 4 (arity p) == 4 then applyEq' p [a,b,c,d] else arityError p [a,b,c,d]
+apply5 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> atom
+apply5 p a b c d e = if fromMaybe 5 (arity p) == 5 then applyEq' p [a,b,c,d,e] else arityError p [a,b,c,d,e]
+apply6 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> atom
+apply6 p a b c d e f = if fromMaybe 6 (arity p) == 6 then applyEq' p [a,b,c,d,e,f] else arityError p [a,b,c,d,e,f]
+apply7 :: AtomEq atom p a => p -> a -> a -> a -> a -> a -> a -> a -> atom
+apply7 p a b c d e f g = if fromMaybe 7 (arity p) == 7 then applyEq' p [a,b,c,d,e,f,g] else arityError p [a,b,c,d,e,f,g]
+
+arityError :: (Arity p) => p -> [a] -> t
+arityError _p _ts = error "arity error"
+-- arityError :: (Arity p, Pretty p) => p -> [a] -> t
+-- arityError p ts = error $ "arity error: " ++ show (length ts) ++ " arguments applied to arity " ++ show (arity p) ++ " predicate " ++ show (pretty p)
+
+pApp :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> [term] -> formula
+pApp p ts = atomic (applyEq p ts)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a)
+pApp2 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b)
+pApp3 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c)
+pApp4 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d)
+pApp5 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e)
+pApp6 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f)
+pApp7 :: (FirstOrderFormula formula atom v, AtomEq atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g)
+
+showFirstOrderFormulaEq :: (FirstOrderFormula fof atom v, AtomEq atom p term, Show term, Show v, Show p) => fof -> String
+showFirstOrderFormulaEq fm =
+    fst (sfo fm)
+    where
+      sfo p = foldFirstOrder qu co tf pr p
+      qu op v f = (showQuant op ++ " " ++ show v ++ " " ++ parens quantPrec (sfo f), quantPrec)
+      co ((:~:) p) =
+          let prec' = 5 in
+          ("(.~.)" ++ parens prec' (sfo p), prec')
+      co (BinOp p op q) = (parens (opPrec op) (sfo p) ++ " " ++ showBinOp op ++ " " ++ parens (opPrec op) (sfo q), opPrec op)
+      tf x = (if x then "true" else "false", 0)
+      pr = foldAtomEq (\ p ts -> ("pApp " ++ show p ++ " " ++ show ts, 6))
+                      (\ x -> (if x then "true" else "false", 0))
+                      (\ t1 t2 -> ("(" ++ show t1 ++ ") .=. (" ++ show t2 ++ ")", 6))
+      showBinOp (:<=>:) = ".<=>."
+      showBinOp (:=>:) = ".=>."
+      showBinOp (:&:) = ".&."
+      showBinOp (:|:) = ".|."
+      showQuant Exists = "exists"
+      showQuant Forall = "for_all"
+      opPrec (:|:) = 3
+      opPrec (:&:) = 4
+      opPrec (:=>:) = 2
+      opPrec (:<=>:) = 2
+      quantPrec = 1
+      parens :: Int -> (String, Int) -> String
+      parens prec' (s, prec) = if prec >= prec' then "(" ++ s ++ ")" else s
+
+infix 5 .=., .!=., ≡, ≢
+
+(.=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .=. b = atomic (equals a b)
+
+(.!=.) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+a .!=. b = (.~.) (a .=. b)
+
+(≡) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≡) = (.=.)
+
+(≢) :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+(≢) = (.!=.)
+
+{-
+instance (AtomEq atom p term, Constants atom, Variable v, Term term v f) => Formula atom term v where
+    substitute env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+    allVariables = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+    freeVariables = allVariables
+-}
+
+fromAtomEq :: (AtomEq atom1 p1 term1, Term term1 v1 f1,
+               AtomEq atom2 p2 term2, Term term2 v2 f2, Constants atom2) =>
+              (v1 -> v2) -> (p1 -> p2) -> (f1 -> f2) -> atom1 -> atom2
+fromAtomEq cv cp cf atom =
+    foldAtomEq (\ pr ts -> applyEq (cp pr) (map ct ts))
+               fromBool
+               (\ a b -> ct a `equals` ct b)
+               atom
+    where
+      ct = convertTerm cv cf
+
+showAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f, Show v, Show p, Show f) => atom -> String
+showAtomEq =
+    foldAtomEq (\ p ts -> "(pApp" ++ show (length ts) ++ " (" ++ show p ++ ") (" ++ intercalate ") (" (map showTerm ts) ++ "))")
+               (\ x -> if x then "true" else "false")
+               (\ t1 t2 -> "(" ++ parenTerm t1 ++ " .=. " ++ parenTerm t2 ++ ")")
+    where
+      parenTerm :: term -> String
+      parenTerm x = "(" ++ showTerm x ++ ")"
+
+prettyAtomEq :: (AtomEq atom p term, Term term v f) => (v -> Doc) -> (p -> Doc) -> (f -> Doc) -> Int -> atom -> Doc
+prettyAtomEq pv pp pf prec atom =
+    foldAtomEq (\ p ts -> pp p <> case ts of
+                                    [] -> empty
+                                    _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+               (text . ifElse "true" "false")
+               (\ t1 t2 -> parensIf (prec > 6) (prettyTerm pv pf t1 <+> text "=" <+> prettyTerm pv pf t2))
+               atom
+    where
+      parensIf False = id
+      parensIf _ = parens . nest 1
+
+-- | Return the variables that occur in an instance of AtomEq.
+varAtomEq :: forall atom term v p f. (AtomEq atom p term, Term term v f) => atom -> Set.Set v
+varAtomEq = foldAtomEq (\ _ args -> Set.unions (map fvt args)) (const Set.empty) (\ t1 t2 -> Set.union (fvt t1) (fvt t2))
+
+substAtomEq :: (AtomEq atom p term, Constants atom, Term term v f) =>
+               Map.Map v term -> atom -> atom
+substAtomEq env = foldAtomEq (\ p args -> applyEq p (map (tsubst env) args)) fromBool (\ t1 t2 -> equals (tsubst env t1) (tsubst env t2))
+
+funcsAtomEq :: (AtomEq atom p term, Term term v f, Ord f) => atom -> Set.Set (f, Int)
+funcsAtomEq = foldAtomEq (\ _ ts -> Set.unions (map funcs ts)) (const Set.empty) (\ t1 t2 -> Set.union (funcs t1) (funcs t2))
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/FirstOrder.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/FirstOrder.hs
@@ -0,0 +1,354 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+module Data.Logic.Classes.FirstOrder
+    ( FirstOrderFormula(..)
+    , Quant(..)
+    , zipFirstOrder
+    , pApp
+    , pApp0
+    , pApp1
+    , pApp2
+    , pApp3
+    , pApp4
+    , pApp5
+    , pApp6
+    , pApp7
+    , for_all'
+    , exists'
+    , quant
+    , (!)
+    , (?)
+    , (∀)
+    , (∃)
+    , quant'
+    , convertFOF
+    , toPropositional
+    , withUnivQuants
+    , showFirstOrder
+    , prettyFirstOrder
+    , fixityFirstOrder
+    , foldAtomsFirstOrder
+    , mapAtomsFirstOrder
+    , onatoms
+    , overatoms
+    , atom_union
+    , fromFirstOrder
+    , fromLiteral
+    ) where
+
+
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (apply, Apply(..), apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7)
+import Data.Logic.Classes.Combine (binop, BinOp((:&:), (:<=>:), (:=>:), (:|:)), Combinable, Combination(..), combine)
+import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Failing (Failing(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set as Set (empty, Set, union)
+import Text.PrettyPrint ((<+>), (<>), Doc, nest, parens, text)
+
+
+-- |The 'FirstOrderFormula' type class.  Minimal implementation:
+-- @for_all, exists, foldFirstOrder, foldTerm, (.=.), pApp0-pApp7, fApp, var@.  The
+-- functional dependencies are necessary here so we can write
+-- functions that don't fix all of the type parameters.  For example,
+-- without them the univquant_free_vars function gives the error @No
+-- instance for (FirstOrderFormula Formula atom V)@ because the
+-- function doesn't mention the Term type.
+class ( Formula formula atom
+      , Combinable formula  -- Basic logic operations
+      , Constants formula
+      , Constants atom
+      , HasFixity atom
+      , Variable v
+      , Pretty atom, Pretty v
+      ) => FirstOrderFormula formula atom v | formula -> atom v where
+    -- | Universal quantification - for all x (formula x)
+    for_all :: v -> formula -> formula
+    -- | Existential quantification - there exists x such that (formula x)
+    exists ::  v -> formula -> formula
+
+    -- | A fold function similar to the one in 'PropositionalFormula'
+    -- but extended to cover both the existing formula types and the
+    -- ones introduced here.  @foldFirstOrder (.~.) quant binOp infixPred pApp@
+    -- is a no op.  The argument order is taken from Logic-TPTP.
+    foldFirstOrder :: (Quant -> v -> formula -> r)
+                   -> (Combination formula -> r)
+                   -> (Bool -> r)
+                   -> (atom -> r)
+                   -> formula
+                   -> r
+
+zipFirstOrder :: FirstOrderFormula formula atom v =>
+                 (Quant -> v -> formula -> Quant -> v -> formula -> Maybe r)
+              -> (Combination formula -> Combination formula -> Maybe r)
+              -> (Bool -> Bool -> Maybe r)
+              -> (atom -> atom -> Maybe r)
+              -> formula -> formula -> Maybe r
+zipFirstOrder qu co tf at fm1 fm2 =
+    foldFirstOrder qu' co' tf' at' fm1
+    where
+      qu' op1 v1 p1 = foldFirstOrder (qu op1 v1 p1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      co' c1 = foldFirstOrder (\ _ _ _ -> Nothing) (co c1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' atom1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at atom1) fm2
+
+-- |The 'Quant' and 'InfixPred' types, like the BinOp type in
+-- 'Data.Logic.Propositional', could be additional parameters to the type
+-- class, but it would add additional complexity with unclear
+-- benefits.
+data Quant = Forall | Exists deriving (Eq,Ord,Show,Read,Data,Typeable,Enum,Bounded)
+
+pApp :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> [term] -> formula
+pApp p ts = atomic (apply p ts :: atom)
+
+-- | Versions of pApp specialized for different argument counts.
+pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> formula
+pApp0 p = atomic (apply0 p :: atom)
+pApp1 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> formula
+pApp1 p a = atomic (apply1 p a :: atom)
+pApp2 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> formula
+pApp2 p a b = atomic (apply2 p a b :: atom)
+pApp3 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> formula
+pApp3 p a b c = atomic (apply3 p a b c :: atom)
+pApp4 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> formula
+pApp4 p a b c d = atomic (apply4 p a b c d :: atom)
+pApp5 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> formula
+pApp5 p a b c d e = atomic (apply5 p a b c d e :: atom)
+pApp6 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
+pApp6 p a b c d e f = atomic (apply6 p a b c d e f :: atom)
+pApp7 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
+pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g :: atom)
+
+-- |for_all with a list of variables, for backwards compatibility.
+for_all' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+for_all' vs f = foldr for_all f vs
+
+-- |exists with a list of variables, for backwards compatibility.
+exists' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
+exists' vs f = foldr for_all f vs
+
+-- |Names for for_all and exists inspired by the conventions of the
+-- TPTP project.
+(!) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(!) = for_all
+(?) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(?) = exists
+
+-- Irrelevant, because these are always used as prefix operators, never as infix.
+infixr 9 !, ?, ∀, ∃
+
+-- | ∀ can't be a function when -XUnicodeSyntax is enabled.
+(∀) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∀) = for_all
+(∃) :: FirstOrderFormula formula atom v => v -> formula -> formula
+(∃) = exists
+
+-- | Helper function for building folds.
+quant :: FirstOrderFormula formula atom v => 
+         Quant -> v -> formula -> formula
+quant Forall v f = for_all v f
+quant Exists v f = exists v f
+
+-- |Legacy version of quant from when we supported lists of quantified
+-- variables.  It also has the virtue of eliding quantifications with
+-- empty variable lists (by calling for_all' and exists'.)
+quant' :: FirstOrderFormula formula atom v => 
+         Quant -> [v] -> formula -> formula
+quant' Forall = for_all'
+quant' Exists = exists'
+
+convertFOF :: (FirstOrderFormula formula1 atom1 v1, FirstOrderFormula formula2 atom2 v2) =>
+              (atom1 -> atom2) -> (v1 -> v2) -> formula1 -> formula2
+convertFOF convertA convertV formula =
+    foldFirstOrder qu co tf (atomic . convertA) formula
+    where
+      convert' = convertFOF convertA convertV
+      qu x v f = quant x (convertV v) (convert' f)
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+
+-- |Try to convert a first order logic formula to propositional.  This
+-- will return Nothing if there are any quantifiers, or if it runs
+-- into an atom that it is unable to convert.
+toPropositional :: forall formula1 atom v formula2 atom2.
+                   (FirstOrderFormula formula1 atom v,
+                    P.PropositionalFormula formula2 atom2) =>
+                   (atom -> atom2) -> formula1 -> formula2
+toPropositional convertAtom formula =
+    foldFirstOrder qu co tf at formula
+    where
+      convert' = toPropositional convertAtom
+      qu _ _ _ = error "toPropositional: invalid argument"
+      co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      co ((:~:) f) = combine ((:~:) (convert' f))
+      tf = fromBool
+      at = atomic . convertAtom
+
+-- | Display a formula in a format that can be read into the interpreter.
+showFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v, Show v) => (atom -> String) -> formula -> String
+showFirstOrder sa formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu Forall v f = "(for_all " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
+      qu Exists v f = "(exists " ++  show v ++ " " ++ showFirstOrder sa f ++ ")"
+      co (BinOp f1 op f2) = "(" ++ parenForm f1 ++ " " ++ showCombine op ++ " " ++ parenForm f2 ++ ")"
+      co ((:~:) f) = "((.~.) " ++ showFirstOrder sa f ++ ")"
+      tf x = if x then "true" else "false"
+      at :: atom -> String
+      at = sa
+      parenForm x = "(" ++ showFirstOrder sa x ++ ")"
+      showCombine (:<=>:) = ".<=>."
+      showCombine (:=>:) = ".=>."
+      showCombine (:&:) = ".&."
+      showCombine (:|:) = ".|."
+
+prettyFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v) =>
+                      (Int -> atom -> Doc) -> (v -> Doc) -> Int -> formula -> Doc
+prettyFirstOrder pa pv pprec formula =
+    parensIf (pprec > prec) $
+    foldFirstOrder
+          (\ qop v f -> prettyQuant qop <> pv v <> text "." <+> (prettyFirstOrder pa pv prec f))
+          (\ cm ->
+               case cm of
+                 (BinOp f1 op f2) ->
+                     case op of
+                       (:=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:<=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:&:) -> (prettyFirstOrder pa pv 3 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                       (:|:) -> (prettyFirstOrder pa pv 4 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
+                 ((:~:) f) -> text "¬" {-"~"-} <> prettyFirstOrder pa pv prec f)
+          (text . ifElse "true" "false")
+          (pa prec)
+          formula
+    where
+      Fixity prec _ = fixityFirstOrder formula
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      prettyQuant Forall = text "∀" -- "!"
+      prettyQuant Exists = text "∃" -- "?"
+
+fixityFirstOrder :: (HasFixity atom, FirstOrderFormula formula atom v) => formula -> Fixity
+fixityFirstOrder formula =
+    foldFirstOrder qu co tf at formula
+    where
+      qu _ _ _ = Fixity 10 InfixN
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- | Examine the formula to find the list of outermost universally
+-- quantified variables, and call a function with that list and the
+-- formula after the quantifiers are removed.
+withUnivQuants :: FirstOrderFormula formula atom v => ([v] -> formula -> r) -> formula -> r
+withUnivQuants fn formula =
+    doFormula [] formula
+    where
+      doFormula vs f =
+          foldFirstOrder
+                (doQuant vs)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                (\ _ -> fn (reverse vs) f)
+                f
+      doQuant vs Forall v f = doFormula (v : vs) f
+      doQuant vs Exists v f = fn (reverse vs) (exists v f)
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+mapAtomsFirstOrder :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+mapAtomsFirstOrder f fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op v p = quant op v (mapAtomsFirstOrder f p)
+      co ((:~:) p) = mapAtomsFirstOrder f p
+      co (BinOp p op q) = binop (mapAtomsFirstOrder f p) op (mapAtomsFirstOrder f q)
+      tf flag = fromBool flag
+      at x = f x
+
+-- | Deprecated - use mapAtoms
+onatoms :: forall formula atom v. (FirstOrderFormula formula atom v) => (atom -> formula) -> formula -> formula
+onatoms = mapAtomsFirstOrder
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- -------------------------------------------------------------------------
+
+foldAtomsFirstOrder :: FirstOrderFormula fof atom v => (r -> atom -> r) -> r -> fof -> r
+foldAtomsFirstOrder f i fof =
+        foldFirstOrder qu co (const i) (f i) fof
+        where
+          qu _ _ fof' = foldAtomsFirstOrder f i fof'
+          co ((:~:) fof') = foldAtomsFirstOrder f i fof'
+          co (BinOp p _ q) = foldAtomsFirstOrder f (foldAtomsFirstOrder f i q) p
+
+-- | Deprecated - use foldAtoms
+overatoms :: forall formula atom v r. FirstOrderFormula formula atom v =>
+             (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsFirstOrder (flip f) b fm
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: forall formula atom v a. (FirstOrderFormula formula atom v, Ord a) =>
+              (atom -> Set.Set a) -> formula -> Set.Set a
+atom_union f fm = overatoms (\ h t -> Set.union (f h) t) fm Set.empty
+
+$(deriveSafeCopy 1 'base ''Quant)
+
+
+{- This makes bad things happen.
+-- | We can use an fof type as a lit, but it must not use some constructs.
+instance FirstOrderFormula fof atom v => Literal fof atom v where
+    foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm
+        where qu = error "instance Literal FirstOrderFormula"
+              co ((:~:) x) = neg x
+              co _ = error "instance Literal FirstOrderFormula"
+    atomic = Data.Logic.Classes.FirstOrder.atomic
+-}
+
+-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
+-- with a construct unsupported in a normal logic formula,
+-- i.e. quantifiers and formula combinators other than negation.
+ 
+fromFirstOrder ::
+               forall formula atom v lit atom2 .
+                   (Formula lit atom2,
+                    Data.Logic.Classes.FirstOrder.FirstOrderFormula formula atom v,
+                    Literal lit atom2) =>
+                   (atom -> atom2) -> formula -> Failing lit
+fromFirstOrder ca formula
+  = Data.Logic.Classes.FirstOrder.foldFirstOrder
+      (\ _ _ _ -> Failure ["fromFirstOrder"])
+      co
+      (Success . fromBool)
+      (Success . atomic . ca)
+      formula
+  where  
+        co :: Combination formula -> Failing lit
+        co ((:~:) f) = fromFirstOrder ca f >>= return . (.~.)
+        co _ = Failure ["fromFirstOrder"]
+
+
+ 
+fromLiteral ::
+            forall lit atom v fof atom2 .
+                (Literal lit atom,
+                 Data.Logic.Classes.FirstOrder.FirstOrderFormula fof atom2 v) =>
+                (atom -> atom2) -> lit -> fof
+fromLiteral ca lit = foldLiteral (\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit
+
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Formula.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Formula.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Formula.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Data.Logic.Classes.Formula
+    ( Formula(atomic, foldAtoms, mapAtoms)
+    ) where
+
+class Formula formula atom where
+    atomic :: atom -> formula
+    foldAtoms :: Formula formula atom => (r -> atom -> r) -> r -> formula -> r
+    mapAtoms :: Formula formula atom => (atom -> formula) -> formula -> formula
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Literal.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Literal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Literal.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -Wwarn #-}
+module Data.Logic.Classes.Literal
+    ( Literal(foldLiteral)
+    , zipLiterals
+    , toPropositional
+    , prettyLit
+    , fixityLiteral
+    , foldAtomsLiteral
+    ) where
+
+
+
+import Data.Logic.Classes.Constants (Constants, Constants(fromBool))
+import Data.Logic.Classes.Formula (Formula, Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Classes.Pretty (Fixity(Fixity), FixityDirection(InfixN), HasFixity, HasFixity(fixity))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
+import Text.PrettyPrint ((<>), Doc, nest, parens, text)
+
+
+
+-- |Literals are the building blocks of the clause and implicative normal
+-- |forms.  They support negation and must include True and False elements.
+class (Negatable lit, Constants lit, HasFixity atom, Formula lit atom, Ord lit) => Literal lit atom | lit -> atom where
+    foldLiteral :: (lit -> r) -> (Bool -> r) -> (atom -> r) -> lit -> r
+
+
+zipLiterals :: Literal lit atom =>
+               (lit -> lit -> Maybe r)
+            -> (Bool -> Bool -> Maybe r)
+            -> (atom -> atom -> Maybe r)
+            -> lit -> lit -> Maybe r
+zipLiterals neg tf at fm1 fm2 =
+    foldLiteral neg' tf' at' fm1
+    where
+      neg' p1 = foldLiteral (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
+      tf' x1 = foldLiteral (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
+      at' a1 = foldLiteral (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
+
+
+toPropositional :: forall lit atom pf atom2. (Literal lit atom, P.PropositionalFormula pf atom2) =>
+                   (atom -> atom2) -> lit -> pf
+toPropositional ca lit = foldLiteral (\ p -> (.~.) (toPropositional ca p)) fromBool (atomic . ca) lit
+
+
+{-
+prettyLit :: forall lit atom term v p f. (Literal lit atom v, Apply atom p term, Term term v f) =>
+              (v -> Doc)
+           -> (p -> Doc)
+           -> (f -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pv pp pf _prec lit =
+    foldLiteral neg tf at lit
+    where
+      neg :: lit -> Doc
+      neg x = if negated x then text {-"¬"-} "~" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x
+      tf = text . ifElse "true" "false"
+      at = foldApply (\ pr ts -> 
+                        pp pr <> case ts of
+                                   [] -> empty
+                                   _ -> parens (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts))))
+                   (\ x -> text $ if x then "true" else "false")
+      -- parensIf False = id
+      -- parensIf _ = parens . nest 1
+-}
+
+prettyLit :: forall lit atom v. (Literal lit atom) =>
+              (Int -> atom -> Doc)
+           -> (v -> Doc)
+           -> Int
+           -> lit
+           -> Doc
+prettyLit pa pv pprec lit =
+    parensIf (pprec > prec) $ foldLiteral co tf at lit
+    where
+      co :: lit -> Doc
+      co x = if negated x then text {-"¬"-} "~" <> prettyLit pa pv 5 x else prettyLit pa pv 5 x
+      tf x = text (if x then "true" else "false")
+      at = pa 6
+      parensIf False = id
+      parensIf _ = parens . nest 1
+      Fixity prec _ = fixityLiteral lit
+
+
+fixityLiteral :: (Literal formula atom) => formula -> Fixity
+fixityLiteral formula =
+    foldLiteral neg tf at formula
+    where
+      neg _ = Fixity 5 InfixN
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+
+foldAtomsLiteral :: Literal lit atom => (r -> atom -> r) -> r -> lit -> r
+foldAtomsLiteral f i lit = foldLiteral (foldAtomsLiteral f i) (const i) (f i) lit
+
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Negate.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Negate.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Negate.hs
@@ -0,0 +1,42 @@
+module Data.Logic.Classes.Negate
+     ( Negatable(..)
+     , negated
+     , (.~.)
+     , (¬)
+     , negative
+     , positive
+     ) where
+
+-- |The class of formulas that can be negated.  There are some types
+-- that can be negated but do not support the other Boolean Logic
+-- operators, such as the 'Literal' class.
+class Negatable formula where
+    -- | Negate a formula in a naive fashion, the operators below
+    -- prevent double negation.
+    negatePrivate :: formula -> formula
+    -- | Test whether a formula is negated or normal
+    foldNegation :: (formula -> r) -- ^ called for normal formulas
+                 -> (formula -> r) -- ^ called for negated formulas
+                 -> formula -> r
+-- | Is this formula negated at the top level?
+negated :: Negatable formula => formula -> Bool
+negated = foldNegation (const False) (not . negated)
+
+-- | Negate the formula, avoiding double negation
+(.~.) :: Negatable formula => formula -> formula
+(.~.) = foldNegation negatePrivate id
+
+(¬) :: Negatable formula => formula -> formula
+(¬) = (.~.)
+
+infix 5 .~., ¬
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.  (These names are used in Harrison's code.)
+-- ------------------------------------------------------------------------- 
+
+negative :: Negatable formula => formula -> Bool
+negative = negated
+
+positive :: Negatable formula => formula -> Bool
+positive = not . negative
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Pretty.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Pretty.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Classes.Pretty
+    ( Pretty(pretty)
+    , HasFixity(fixity)
+    , TH.Fixity(..)
+    , TH.FixityDirection(..)
+    , topFixity
+    , botFixity
+    ) where
+
+import qualified Language.Haskell.TH.Syntax as TH
+import Text.PrettyPrint (Doc, text)
+
+-- | The intent of this class is to be similar to Show, but only one
+-- way, with no corresponding Read class.  It doesn't really belong
+-- here in logic-classes.  To put something in a pretty printing class
+-- implies that there is only one way to pretty print it, which is not
+-- an assumption made by Text.PrettyPrint.  But in practice this is
+-- often good enough.
+class Pretty x where
+    pretty :: x -> Doc
+
+-- | A class used to do proper parenthesization of formulas.  If we
+-- nest a higher precedence formula inside a lower one parentheses can
+-- be omitted.  Because @|@ has lower precedence than @&@, the formula
+-- @a | (b & c)@ appears as @a | b & c@, while @(a | b) & c@ appears
+-- unchanged.  (Name Precedence chosen because Fixity was taken.)
+-- 
+-- The second field of Fixity is the FixityDirection, which can be
+-- left, right, or non.  A left associative operator like @/@ is
+-- grouped left to right, so parenthese can be omitted from @(a / b) /
+-- c@ but not from @a / (b / c)@.  It is a syntax error to omit
+-- parentheses when formatting a non-associative operator.
+-- 
+-- The Haskell FixityDirection type is concerned with how to interpret
+-- a formula formatted in a certain way, but here we are concerned
+-- with how to format a formula given its interpretation.  As such,
+-- one case the Haskell type does not capture is whether the operator
+-- follows the associative law, so we can omit parentheses in an
+-- expression such as @a & b & c@.
+class HasFixity x where
+    fixity :: x -> TH.Fixity
+
+-- Definitions from template-haskell:
+-- data Fixity = Fixity Int FixityDirection
+-- data FixityDirection = InfixL | InfixR | InfixN
+
+-- | This is used as the initial value for the parent fixity.
+topFixity :: TH.Fixity
+topFixity = TH.Fixity 0 TH.InfixN
+
+-- | This is used as the fixity for things that never need
+-- parenthesization, such as function application.
+botFixity :: TH.Fixity
+botFixity = TH.Fixity 10 TH.InfixN
+
+instance Pretty String where
+    pretty = text
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Propositional.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Propositional.hs
@@ -0,0 +1,333 @@
+-- | PropositionalFormula is a multi-parameter type class for
+-- representing instance of propositional (aka zeroth order) logic
+-- datatypes.  These are formulas which have truth values, but no "for
+-- all" or "there exists" quantifiers and thus no variables or terms
+-- as we have in first order or predicate logic.  It is intended that
+-- we will be able to write instances for various different
+-- implementations to allow these systems to interoperate.  The
+-- operator names were adopted from the Logic-TPTP package.
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Classes.Propositional
+    ( PropositionalFormula(..)
+    , showPropositional
+    , prettyPropositional
+    , fixityPropositional
+    , convertProp
+    , combine
+    , negationNormalForm
+    , clauseNormalForm
+    , clauseNormalForm'
+    , clauseNormalFormAlt
+    , clauseNormalFormAlt'
+    , disjunctiveNormalForm
+    , disjunctiveNormalForm'
+    , overatoms
+    , foldAtomsPropositional
+    , mapAtomsPropositional
+    ) where
+
+import Data.Logic.Classes.Combine
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool, prettyBool)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate
+import Data.Logic.Classes.Pretty (Pretty, HasFixity(fixity), Fixity(Fixity), FixityDirection(..))
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as Set
+import Text.PrettyPrint (Doc, text, (<>))
+
+-- |A type class for propositional logic.  If the type we are writing
+-- an instance for is a zero-order (aka propositional) logic type
+-- there will generally by a type or a type parameter corresponding to
+-- atom.  For first order or predicate logic types, it is generally
+-- easiest to just use the formula type itself as the atom type, and
+-- raise errors in the implementation if a non-atomic formula somehow
+-- appears where an atomic formula is expected (i.e. as an argument to
+-- atomic or to the third argument of foldPropositional.)
+-- 
+-- The Ord superclass is required so we can put formulas in sets
+-- during the normal form computations.  Negatable and Combinable are
+-- also considered basic operations that we can't build this package
+-- without.  It is less obvious whether Constants is always required,
+-- but the implementation of functions like simplify would be more
+-- elaborate if we didn't have it, so we will require it.
+class (Ord formula, Negatable formula, Combinable formula, Constants formula,
+       Pretty formula, HasFixity formula, Formula formula atom) => PropositionalFormula formula atom | formula -> atom where
+    -- | Build an atomic formula from the atom type.
+    -- | A fold function that distributes different sorts of formula
+    -- to its parameter functions, one to handle binary operators, one
+    -- for negations, and one for atomic formulas.  See examples of its
+    -- use to implement the polymorphic functions below.
+    foldPropositional :: (Combination formula -> r)
+                      -> (Bool -> r)
+                      -> (atom -> r)
+                      -> formula -> r
+
+-- | Show a formula in a format that can be evaluated 
+showPropositional :: (PropositionalFormula formula atom) => (atom -> String) -> formula -> String
+showPropositional showAtom formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) f) = "(.~.) " ++ parenForm f
+      co (BinOp f1 op f2) = parenForm f1 ++ " " ++ showFormOp op ++ " " ++ parenForm f2
+      tf True = "true"
+      tf False = "false"
+      at = showAtom
+      parenForm x = "(" ++ showPropositional showAtom x ++ ")"
+      showFormOp (:<=>:) = ".<=>."
+      showFormOp (:=>:) = ".=>."
+      showFormOp (:&:) = ".&."
+      showFormOp (:|:) = ".|."
+
+-- | Show a formula in a visually pleasing format.
+prettyPropositional :: (PropositionalFormula formula atom, HasFixity formula) =>
+                       (atom -> Doc)
+                    -> Fixity        -- ^ The fixity of the parent formula.  If the operator being formatted here
+                                     -- has a lower precedence it needs to be parenthesized.
+                    -> formula
+                    -> Doc
+prettyPropositional prettyAtom (Fixity pprec _pdir) formula =
+    parenIf (pprec > prec) (foldPropositional co tf at formula)
+    where
+      co ((:~:) f) = text "¬" <> prettyPropositional prettyAtom fix f
+      co (BinOp f1 op f2) = prettyPropositional prettyAtom fix f1 <> text " " <> prettyBinOp op <> text " " <> prettyPropositional prettyAtom fix f2
+      tf = prettyBool
+      at = prettyAtom
+      -- parenForm x = cat [text "(", prettyPropositional prettyAtom 0 x, text ")"]
+      parenIf True x = text "(" <> x <> text ")"
+      parenIf False x = x
+      fix@(Fixity prec _dir) = fixity formula
+
+fixityPropositional :: (HasFixity atom, PropositionalFormula formula atom) => formula -> Fixity
+fixityPropositional formula =
+    foldPropositional co tf at formula
+    where
+      co ((:~:) _) = Fixity 5 InfixN
+      co (BinOp _ (:&:) _) = Fixity 4 InfixL
+      co (BinOp _ (:|:) _) = Fixity 3 InfixL
+      co (BinOp _ (:=>:) _) = Fixity 2 InfixR
+      co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
+      tf _ = Fixity 10 InfixN
+      at = fixity
+
+-- |Convert any instance of a propositional logic expression to any
+-- other using the supplied atom conversion function.
+convertProp :: forall formula1 atom1 formula2 atom2.
+               (PropositionalFormula formula1 atom1,
+                PropositionalFormula formula2 atom2) =>
+               (atom1 -> atom2) -> formula1 -> formula2
+convertProp convertA formula =
+    foldPropositional c fromBool a formula
+    where
+      convert' = convertProp convertA
+      c ((:~:) f) = (.~.) (convert' f)
+      c (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
+      a = atomic . convertA
+
+-- | Simplify and recursively apply nnf.
+negationNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+negationNormalForm = nnf . psimplify
+
+-- |Eliminate => and <=> and move negations inwards:
+-- 
+-- @
+-- Formula      Rewrites to
+--  P => Q      ~P | Q
+--  P <=> Q     (P & Q) | (~P & ~Q)
+-- ~∀X P        ∃X ~P
+-- ~∃X P        ∀X ~P
+-- ~(P & Q)     (~P | ~Q)
+-- ~(P | Q)     (~P & ~Q)
+-- ~~P  P
+-- @
+-- 
+nnf :: (PropositionalFormula formula atom) => formula -> formula
+nnf fm = foldPropositional (nnfCombine fm) fromBool (\ _ -> fm) fm
+
+nnfCombine :: (PropositionalFormula formula atom) => formula -> Combination formula -> formula
+nnfCombine fm ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+nnfCombine _ (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+nnfCombine _ (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+nnfCombine _ (BinOp p (:&:) q) = nnf p .&. nnf q
+nnfCombine _ (BinOp p (:|:) q) = nnf p .|. nnf q
+
+nnfNotCombine :: (PropositionalFormula formula atom) => Combination formula -> formula
+nnfNotCombine ((:~:) p) = nnf p
+nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- |Do a bottom-up recursion to simplify a propositional formula.
+psimplify :: (PropositionalFormula formula atom) => formula -> formula
+psimplify fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      co (BinOp p (:&:) q) = psimplify1 (psimplify p .&. psimplify q)
+      co (BinOp p (:|:) q) = psimplify1 (psimplify p .|. psimplify q)
+      co (BinOp p (:=>:) q) = psimplify1 (psimplify p .=>. psimplify q)
+      co (BinOp p (:<=>:) q) = psimplify1 (psimplify p .<=>. psimplify q)
+      tf _ = fm
+      at _ = fm
+
+-- |Do one step of simplify for propositional formulas:
+-- Perform the following transformations everywhere, plus any
+-- commuted versions for &, |, and <=>.
+-- 
+-- @
+--  ~False      -> True
+--  ~True       -> False
+--  True & P    -> P
+--  False & P   -> False
+--  True | P    -> True
+--  False | P   -> P
+--  True => P   -> P
+--  False => P  -> True
+--  P => True   -> P
+--  P => False  -> True
+--  True <=> P  -> P
+--  False <=> P -> ~P
+-- @
+-- 
+psimplify1 :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) f) = foldPropositional simplifyNotCombine (fromBool . not) simplifyNotAtom f
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _)            -> r
+            (Just False, (:&:), _)            -> fromBool False
+            (_,          (:&:), Just True)    -> l
+            (_,          (:&:), Just False)   -> fromBool False
+            (Just True,  (:|:), _)            -> fromBool True
+            (Just False, (:|:), _)            -> r
+            (_,          (:|:), Just True)    -> fromBool True
+            (_,          (:|:), Just False)   -> l
+            (Just True,  (:=>:), _)           -> r
+            (Just False, (:=>:), _)           -> fromBool True
+            (_,          (:=>:), Just True)   -> fromBool True
+            (_,          (:=>:), Just False)  -> (.~.) l
+            (Just False, (:<=>:), Just False) -> fromBool True
+            (Just True,  (:<=>:), _)          -> r
+            (Just False, (:<=>:), _)          -> (.~.) r
+            (_,          (:<=>:), Just True)  -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _                                 -> fm
+      simplifyNotCombine ((:~:) f) = f
+      simplifyNotCombine _ = fm
+      simplifyNotAtom x = (.~.) (atomic x)
+
+clauseNormalForm' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalForm' = simp purecnf . negationNormalForm
+
+clauseNormalForm :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalForm formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalForm' formula
+
+-- |I'm not sure of the clauseNormalForm functions above are wrong or just different.
+clauseNormalFormAlt' :: (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+clauseNormalFormAlt' = simp purecnf' . negationNormalForm
+
+clauseNormalFormAlt :: forall formula atom. (PropositionalFormula formula atom) => formula -> formula
+clauseNormalFormAlt formula =
+    case clean (lists cnf) of
+      [] -> fromBool True
+      xss -> foldr1 (.&.) . map (foldr1 (.|.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      cnf = clauseNormalFormAlt' formula
+
+disjunctiveNormalForm :: (PropositionalFormula formula atom) => formula -> formula
+disjunctiveNormalForm formula =
+    case clean (lists dnf) of
+      [] -> fromBool False
+      xss -> foldr1 (.|.) . map (foldr1 (.&.)) $ xss
+    where
+      clean = filter (not . null)
+      lists = Set.toList . Set.map Set.toList
+      dnf = disjunctiveNormalForm' formula
+
+disjunctiveNormalForm' :: (PropositionalFormula formula atom, Eq formula) => formula -> Set.Set (Set.Set formula)
+disjunctiveNormalForm' = simp purednf . negationNormalForm
+
+simp :: forall formula atom. (PropositionalFormula formula atom) =>
+        (formula -> Set.Set (Set.Set formula)) -> formula -> Set.Set (Set.Set formula)
+simp purenf fm =
+    case (compare fm (fromBool False), compare fm (fromBool True)) of
+      (EQ, _) -> Set.empty
+      (_, EQ) -> Set.singleton Set.empty
+      _ ->cjs'
+    where
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (Set.isProperSubsetOf x) cjs))
+      cjs = Set.filter (not . trivial) (purenf (nnf fm)) :: Set.Set (Set.Set formula)
+
+-- |Harrison page 59.  Look for complementary pairs in a clause.
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection (Set.map (.~.) n) p
+    where (n, p) = Set.partition negated lits
+
+purecnf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+purednf :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purednf fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.distrib (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+purecnf' :: forall formula atom. (PropositionalFormula formula atom) => formula -> Set.Set (Set.Set formula)
+purecnf' fm =
+    foldPropositional c (\ _ -> x) (\ _ -> x)  fm
+    where
+      c :: Combination formula -> Set.Set (Set.Set formula)
+      c (BinOp p (:&:) q) = Set.union (purecnf' p) (purecnf' q)
+      c (BinOp p (:|:) q) = Set.distrib (purecnf' p) (purecnf' q)
+      c _ = x
+      x :: Set.Set (Set.Set formula)
+      x = Set.singleton (Set.singleton (convertProp id fm)) :: Set.Set (Set.Set formula)
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+-- | Use this to implement foldAtoms
+foldAtomsPropositional :: PropositionalFormula pf atom => (r -> atom -> r) -> r -> pf -> r
+foldAtomsPropositional f i pf =
+        foldPropositional co (const i) (f i) pf
+        where
+          co ((:~:) pf') = foldAtomsPropositional f i pf'
+          co (BinOp p _ q) = foldAtomsPropositional f (foldAtomsPropositional f i q) p
+
+-- | Deprecated - use foldAtoms.
+overatoms :: forall formula atom r. PropositionalFormula formula atom => (atom -> r -> r) -> formula -> r -> r
+overatoms f fm b = foldAtomsPropositional (flip f) b fm
+
+mapAtomsPropositional :: forall formula atom. PropositionalFormula formula atom => (atom -> formula) -> formula -> formula
+mapAtomsPropositional f fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = mapAtomsPropositional f p
+      co (BinOp p op q) = binop (mapAtomsPropositional f p) op (mapAtomsPropositional f q)
+      tf flag = fromBool flag
+      at x = f x
+
+$(deriveSafeCopy 1 'base ''BinOp)
+$(deriveSafeCopy 1 'base ''Combination)
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Skolem.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Skolem.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}
+module Data.Logic.Classes.Skolem where
+
+import Data.Logic.Classes.Variable (Variable)
+
+-- |This class shows how to convert between atomic Skolem functions
+-- and Ints.  We include a variable type as a parameter because we
+-- create skolem functions to replace an existentially quantified
+-- variable, and it can be helpful to retain a reference to the
+-- variable.
+class Variable v => Skolem f v | f -> v where
+    toSkolem :: v -> f
+    -- ^ Built a Skolem function from the given variable and number.
+    -- The number is generally obtained from the skolem monad.
+    isSkolem  :: f -> Bool
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Term.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Term.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Term.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Classes.Term
+    ( Term(..)
+    , Function
+    , convertTerm
+    , showTerm
+    , prettyTerm
+    , fvt
+    , tsubst
+    , funcs
+    ) where
+
+import Data.Generics (Data)
+import Data.List (intercalate, intersperse)
+import Data.Logic.Classes.Pretty (Pretty)
+import Data.Logic.Classes.Skolem
+import Data.Logic.Classes.Variable
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Text.PrettyPrint (Doc, (<>), brackets, hcat, text)
+
+class (Eq f, Ord f, Skolem f v, Data f, Pretty f) => Function f v
+
+class ( Ord term  -- For implementing Ord in Literal
+      , Variable v
+      , Function f v ) => Term term v f | term -> v f where
+    vt :: v -> term
+    -- ^ Build a term which is a variable reference.
+    fApp :: f -> [term] -> term
+    -- ^ Build a term by applying terms to an atomic function.  @f@
+    -- (atomic function) is one of the type parameters, this package
+    -- is mostly indifferent to its internal structure.
+    foldTerm :: (v -> r) -> (f -> [term] -> r) -> term -> r
+    -- ^ A fold for the term data type, which understands terms built
+    -- from a variable and a term built from the application of a
+    -- primitive function to other terms.
+    zipTerms :: (v -> v -> Maybe r) -> (f -> [term] -> f -> [term] -> Maybe r) -> term -> term -> Maybe r
+
+convertTerm :: forall term1 v1 f1 term2 v2 f2.
+               (Term term1 v1 f1,
+                Term term2 v2 f2) =>
+               (v1 -> v2) -> (f1 -> f2) -> term1 -> term2
+convertTerm convertV convertF term =
+    foldTerm v fn term
+    where
+      convertTerm' = convertTerm convertV convertF
+      v = vt . convertV
+      fn x ts = fApp (convertF x) (map convertTerm' ts)
+
+showTerm :: forall term v f. (Term term v f, Show v, Show f) =>
+            term -> String
+showTerm term =
+    foldTerm v f term
+    where
+      v :: v -> String
+      v v' = "vt (" ++ show v' ++ ")"
+      f :: f -> [term] -> String
+      f fn ts = "fApp (" ++ show fn ++ ") [" ++ intercalate "," (map showTerm ts) ++ "]"
+
+prettyTerm :: forall v f term. (Term term v f) =>
+              (v -> Doc)
+           -> (f -> Doc)
+           -> term
+           -> Doc
+prettyTerm pv pf t = foldTerm pv (\ fn ts -> pf fn <> brackets (hcat (intersperse (text ",") (map (prettyTerm pv pf) ts)))) t
+
+fvt :: (Term term v f, Ord v) => term -> Set.Set v
+fvt tm = foldTerm Set.singleton (\ _ args -> Set.unions (map fvt args)) tm
+
+-- ------------------------------------------------------------------------- 
+-- Substitution within terms.                                                
+-- ------------------------------------------------------------------------- 
+
+tsubst :: (Term term v f, Ord v) => Map.Map v term -> term -> term
+tsubst sfn tm = foldTerm (\ x -> fromMaybe tm (Map.lookup x sfn)) (\ fn args -> fApp fn (map (tsubst sfn) args)) tm
+
+funcs :: (Term term v f, Ord f) => term -> Set.Set (f, Int)
+funcs tm =
+    foldTerm (const Set.empty)
+             (\ f args -> foldr (\ arg r -> Set.union (funcs arg) r) (Set.singleton (f, length args)) args)
+             tm
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Classes/Variable.hs b/testdata/split-merge-merge-expected/Data/Logic/Classes/Variable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Classes/Variable.hs
@@ -0,0 +1,32 @@
+module Data.Logic.Classes.Variable
+    ( Variable(..)
+    , variants
+    , showVariable
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Pretty (Pretty)
+import qualified Data.Set as Set
+import Data.String (IsString)
+import Text.PrettyPrint (Doc)
+
+class (Ord v, IsString v, Data v, Pretty v) => Variable v where
+    variant :: v -> Set.Set v -> v
+    -- ^ Return a variable based on v but different from any set
+    -- element.  The result may be v itself if v is not a member of
+    -- the set.
+    prefix :: String -> v -> v
+    -- ^ Modify a variable by adding a prefix.  This unfortunately
+    -- assumes that v is "string-like" but at least one algorithm in
+    -- Harrison currently requires this.
+    prettyVariable :: v -> Doc
+    -- ^ Pretty print a variable
+
+-- | Return an infinite list of variations on v
+variants :: Variable v => v -> [v]
+variants v0 =
+    iter' Set.empty v0
+    where iter' s v = let v' = variant v s in v' : iter' (Set.insert v s) v'
+
+showVariable :: Variable v => v -> String
+showVariable v = "fromString (" ++ show (show (prettyVariable v)) ++ ")"
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Failing.hs b/testdata/split-merge-merge-expected/Data/Logic/Failing.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Failing.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE  DeriveDataTypeable, StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Data.Logic.Failing
+    ( Failing(Success, Failure)
+    , failing
+    ) where
+
+import Control.Applicative.Error
+import Data.Generics
+
+failing :: ([String] -> b) -> (a -> b) -> Failing a -> b
+failing f _ (Failure errs) = f errs
+failing _ f (Success a)    = f a
+
+instance Monad Failing where
+  return = Success
+  m >>= f =
+      case m of
+        (Failure errs) -> (Failure errs)
+        (Success a) -> f a
+  fail errMsg = Failure [errMsg]
+
+deriving instance Typeable1 Failing
+deriving instance Data a => Data (Failing a)
+deriving instance Read a => Read (Failing a)
+deriving instance Eq a => Eq (Failing a)
+deriving instance Ord a => Ord (Failing a)
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/DP.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/DP.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/DP.hs
@@ -0,0 +1,274 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DP
+    ( tests
+    , dpll
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.DefCNF (defcnfs, NumAtom(..))
+import Data.Logic.Harrison.Lib (allpairs, defined, maximize', minimize', setmapfilter, (|->))
+import Data.Logic.Harrison.Prop (negative, positive, trivial)
+import Data.Logic.Harrison.PropExamples (Atom(..), N, prime)
+import Data.Logic.Tests.HUnit (assertEqual, convert, Test(TestCase, TestList))
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (delete, difference, empty, filter, findMin, flatten, fold, insert, intersection, map, member, minView, null, partition, Set, singleton, size, union)
+
+instance NumAtom (Atom N) where
+    ma n = P "p" n Nothing
+    ai (P _ n _) = n
+
+tests = convert (TestList [test01, test02, test03])
+
+-- ========================================================================= 
+-- The Davis-Putnam and Davis-Putnam-Loveland-Logemann procedures.           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- The DP procedure.                                                         
+-- ------------------------------------------------------------------------- 
+
+one_literal_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+one_literal_rule clauses =
+    case Set.minView (Set.filter (\ cl -> Set.size cl == 1) clauses) of
+      Nothing -> Failure ["one_literal_rule"]
+      Just (s, _) ->
+          let u = Set.findMin s in
+          let u' = (.~.) u in
+          let clauses1 = Set.filter (\ cl -> not (Set.member u cl)) clauses in
+          Success (Set.map (\ cl -> Set.delete u' cl) clauses1)
+
+affirmative_negative_rule :: (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+affirmative_negative_rule clauses =
+  let (neg',pos) = Set.partition negative (Set.flatten clauses) in
+  let neg = Set.map (.~.) neg' in
+  let pos_only = Set.difference pos neg
+      neg_only = Set.difference neg pos in
+  let pure = Set.union pos_only (Set.map (.~.) neg_only) in
+  if Set.null pure
+  then Failure ["affirmative_negative_rule"]
+  else Success (Set.filter (\ cl -> Set.null (Set.intersection cl pure)) clauses)
+
+resolve_on :: forall lit atom. (Literal lit atom, Ord lit) =>
+              lit -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit)
+resolve_on p clauses =
+  let p' = (.~.) p
+      (pos,notpos) = Set.partition (Set.member p) clauses in
+  let (neg,other) = Set.partition (Set.member p') notpos in
+  let pos' = Set.map (Set.filter (\ l -> l /= p)) pos
+      neg' = Set.map (Set.filter (\ l -> l /= p')) neg in
+  let res0 = allpairs Set.union pos' neg' in
+  Set.union other (Set.filter (not . trivial) res0)
+
+resolution_blowup :: forall formula. (Negatable formula, Ord formula) =>
+                     Set.Set (Set.Set formula) -> formula -> Int
+resolution_blowup cls l =
+  let m = Set.size (Set.filter (Set.member l) cls)
+      n = Set.size (Set.filter (Set.member ((.~.) l)) cls) in
+  m * n - m - n
+
+resolution_rule :: forall lit atom. (Literal lit atom, Ord lit) =>
+                   Set.Set (Set.Set lit) -> Failing (Set.Set (Set.Set lit))
+resolution_rule clauses =
+    let pvs = Set.filter positive (Set.flatten clauses) in
+    case minimize' (resolution_blowup clauses) pvs of
+      Just p -> Success (resolve_on p clauses)
+      Nothing -> Failure ["resolution_rule"]
+
+-- ------------------------------------------------------------------------- 
+-- Overall procedure.                                                        
+-- ------------------------------------------------------------------------- 
+
+dp :: forall lit atom. (Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> Failing Bool        
+dp clauses =
+  if Set.null clauses
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dp of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dp of
+                    Success x -> Success x
+                    Failure _ -> resolution_rule clauses >>= dp
+
+-- ------------------------------------------------------------------------- 
+-- Davis-Putnam satisfiability tester and tautology checker.                 
+-- ------------------------------------------------------------------------- 
+
+dpsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dpsat fm = dp (defcnfs fm :: Set.Set (Set.Set pf))
+
+dptaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) => pf -> Failing Bool
+dptaut fm = dpsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test01 = TestCase (assertEqual "dptaut(prime 11)" (Success True) (dptaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- The same thing but with the DPLL procedure.                               
+-- ------------------------------------------------------------------------- 
+
+posneg_count :: forall formula. (Negatable formula, Ord formula) =>
+                Set.Set (Set.Set formula) -> formula -> Int
+posneg_count cls l =                         
+  let m = Set.size(Set.filter (Set.member l) cls)                 
+      n = Set.size(Set.filter (Set.member ((.~.) l)) cls) in
+  m + n                                  
+
+dpll :: forall lit atom. (Literal lit atom, Ord lit) =>
+        Set.Set (Set.Set lit) -> Failing Bool
+dpll clauses =       
+  if clauses == Set.empty
+  then Success True
+  else if Set.member Set.empty clauses
+       then Success False
+       else case one_literal_rule clauses >>= dpll of
+              Success x -> Success x
+              Failure _ ->
+                  case affirmative_negative_rule clauses >>= dpll of
+                    Success x -> Success x
+                    Failure _ ->
+                        let pvs = Set.filter positive (Set.flatten clauses) in
+                        case maximize' (posneg_count clauses) pvs of
+                          Nothing -> Failure ["dpll"]
+                          Just p -> 
+                              case (dpll (Set.insert (Set.singleton p) clauses), dpll (Set.insert (Set.singleton ((.~.) p)) clauses)) of
+                                (Success a, Success b) -> Success (a || b)
+                                (Failure a, Failure b) -> Failure (a ++ b)
+                                (Failure a, _) -> Failure a
+                                (_, Failure b) -> Failure b
+
+dpllsat :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+           pf -> Failing Bool
+dpllsat fm = dpll(defcnfs fm :: Set.Set (Set.Set pf))
+
+dplltaut :: forall pf. (PropositionalFormula pf (Atom N), Literal pf (Atom N), Ord pf) =>
+            pf -> Failing Bool
+dplltaut fm = dpllsat ((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+test02 = TestCase (assertEqual "dplltaut(prime 11)" (Success True) (dplltaut(prime 11 :: Formula (Atom N)))) 
+
+-- ------------------------------------------------------------------------- 
+-- Iterative implementation with explicit trail instead of recursion.        
+-- ------------------------------------------------------------------------- 
+
+data TrailMix = Guessed | Deduced deriving (Eq, Ord)
+
+unassigned :: forall formula. (Negatable formula, Ord formula) =>
+              Set.Set (Set.Set formula) -> Set.Set (formula, TrailMix) -> Set.Set formula
+unassigned cls trail =
+    Set.difference (Set.flatten (Set.map (Set.map litabs) cls)) (Set.map (litabs . fst) trail)
+    where litabs p = if negated p then (.~.) p else p
+
+unit_subpropagate :: forall formula. (Negatable formula, Ord formula) =>
+                     (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+                  -> (Set.Set (Set.Set formula), Map.Map formula (), Set.Set (formula, TrailMix))
+unit_subpropagate (cls,fn,trail) =
+  let cls' = Set.map (Set.filter (not . defined fn . (.~.))) cls in
+  let uu cs =
+          case Set.minView cs of
+            Nothing -> Failure ["unit_subpropagate"]
+            Just (c, _) -> if Set.size cs == 1 && not (defined fn c)
+                           then Success cs
+                           else Failure ["unit_subpropagate"] in
+  let newunits = Set.flatten (setmapfilter uu cls') in
+  if Set.null newunits then (cls',fn,trail) else
+  let trail' = Set.fold (\ p t -> Set.insert (p,Deduced) t) trail newunits
+      fn' = Set.fold (\ u -> (u |-> ())) fn newunits in
+  unit_subpropagate (cls',fn',trail')
+
+unit_propagate :: forall t. (Negatable t, Ord t) =>
+                  (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+               -> (Set.Set (Set.Set t), Set.Set (t, TrailMix))
+unit_propagate (cls,trail) =
+  let fn = Set.fold (\ (x,_) -> (x |-> ())) Map.empty trail in
+  let (cls',fn',trail') = unit_subpropagate (cls,fn,trail) in (cls',trail')
+
+backtrack :: forall t. Set.Set (t, TrailMix) -> Set.Set (t, TrailMix)
+backtrack trail =
+  case Set.minView trail of
+    Just ((p,Deduced), tt) -> backtrack tt
+    _ -> trail
+
+dpli :: forall atomic pf. (PropositionalFormula pf atomic, Ord pf) =>
+        Set.Set (Set.Set pf) -> Set.Set (pf, TrailMix) -> Failing Bool
+dpli cls trail =
+  let (cls', trail') = unit_propagate (cls, trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView trail of
+      Just ((p,Guessed), tt) -> dpli cls (Set.insert ((.~.) p, Deduced) tt)
+      _ -> Success False
+  else
+      case unassigned cls (trail' :: Set.Set (pf, TrailMix)) of
+        s | Set.null s -> Success True
+        ps -> case maximize' (posneg_count cls') ps of
+                Just p -> dpli cls (Set.insert (p :: pf, Guessed) trail')
+                Nothing -> Failure ["dpli"]
+
+dplisat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplisat fm = dpli (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplitaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplitaut fm = dplisat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- With simple non-chronological backjumping and learning.                   
+-- ------------------------------------------------------------------------- 
+
+backjump :: forall a. (Negatable a, Ord a) =>
+            Set.Set (Set.Set a) -> a -> Set.Set (a, TrailMix) -> Set.Set (a, TrailMix)
+backjump cls p trail =
+  case Set.minView (backtrack trail) of
+    Just ((q,Guessed), tt) ->
+        let (cls',trail') = unit_propagate (cls, Set.insert (p,Guessed) tt) in
+        if Set.member Set.empty cls' then backjump cls p tt else trail
+    _ -> trail
+
+dplb :: forall a. (Negatable a, Ord a) =>
+        Set.Set (Set.Set a) -> Set.Set (a, TrailMix) -> Failing Bool
+dplb cls trail =
+  let (cls',trail') = unit_propagate (cls,trail) in
+  if Set.member Set.empty cls' then
+    case Set.minView (backtrack trail) of
+      Just ((p,Guessed), tt) ->
+        let trail'' = backjump cls p tt in
+        let declits = Set.filter (\ (_,d) -> d == Guessed) trail'' in
+        let conflict = Set.insert ((.~.) p) (Set.map ((.~.) . fst) declits) in
+        dplb (Set.insert conflict cls) (Set.insert ((.~.) p,Deduced) trail'')
+      _ -> Success False
+  else
+    case unassigned cls trail' of
+      s | Set.null s -> Success True
+      ps -> case maximize' (posneg_count cls') ps of
+              Just p -> dplb cls (Set.insert (p,Guessed) trail')
+              Nothing -> Failure ["dpib"]
+            
+dplbsat :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+           pf -> Failing Bool
+dplbsat fm = dplb (defcnfs fm :: Set.Set (Set.Set pf)) Set.empty
+
+dplbtaut :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom, NumAtom atom, Ord pf) =>
+            pf -> Failing Bool
+dplbtaut fm = dplbsat((.~.) fm) >>= return . not
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+test03 = TestList [TestCase (assertEqual "dplitaut(prime 101)" (Success True) (dplitaut(prime 101 :: Formula (Atom N)))),
+                   TestCase (assertEqual "dplbtaut(prime 101)" (Success True) (dplbtaut(prime 101 :: Formula (Atom N))))]
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/DefCNF.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/DefCNF.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/DefCNF.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-}
+module Data.Logic.Harrison.DefCNF
+where
+
+import Data.Logic.Classes.Combine ((.&.), (.<=>.), (.|.), BinOp(..), Combination(..))
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Propositional (overatoms, PropositionalFormula(foldPropositional))
+import Data.Logic.Harrison.Prop (cnf, nenf, simpcnf)
+import Data.Logic.Harrison.PropExamples (N)
+import qualified Data.Map as Map (elems, empty, insert, lookup, Map)
+import qualified Data.Set.Extra as Set (Set, unions)
+
+-- ========================================================================= 
+-- Definitional CNF.                                                         
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+{-
+START_INTERACTIVE;;
+cnf <<p <=> (q <=> r)>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Make a stylized variable and update the index.                            
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P a
+
+class NumAtom atom where
+    ma :: N -> atom
+    ai :: atom -> N
+
+instance NumAtom (Atom N) where
+    ma = P
+    ai (P n) = n
+
+mkprop :: forall pf atom. (PropositionalFormula pf atom, NumAtom atom) => N -> (pf, N)
+mkprop n = (atomic (ma n :: atom), n + 1)
+
+-- ------------------------------------------------------------------------- 
+-- Core definitional CNF procedure.                                          
+-- ------------------------------------------------------------------------- 
+
+maincnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+maincnf trip@(fm, _defs, _n) =
+    foldPropositional co tf at fm
+    where
+      co (BinOp p (:&:) q) = defstep (.&.) (p,q) trip
+      co (BinOp p (:|:) q) = defstep (.|.) (p,q) trip
+      co (BinOp p (:<=>:) q) = defstep (.<=>.) (p,q) trip
+      co (BinOp _ (:=>:) _) = trip
+      co ((:~:) _) = trip
+      tf _ = trip
+      at _ = trip
+
+defstep :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf -> pf -> pf) -> (pf, pf) -> (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+defstep op (p,q) (_fm, defs, n) =
+  let (fm1,defs1,n1) = maincnf (p,defs,n) in
+  let (fm2,defs2,n2) = maincnf (q,defs1,n1) in
+  let fm' = op fm1 fm2 in
+  case Map.lookup fm' defs2 of
+    Just _ -> (fm', defs2, n2)
+    Nothing -> let (v,n3) = mkprop n2 in (v, Map.insert v (v .<=>. fm') defs2,n3)
+
+-- ------------------------------------------------------------------------- 
+-- Make n large enough that "v_m" won't clash with s for any m >= n          
+-- ------------------------------------------------------------------------- 
+
+max_varindex :: NumAtom atom =>  atom -> Int -> Int
+max_varindex atom n = max n (ai atom)
+
+-- ------------------------------------------------------------------------- 
+-- Overall definitional CNF.                                                 
+-- ------------------------------------------------------------------------- 
+
+mk_defcnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) =>
+             ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)) -> pf -> Set.Set (Set.Set lit)
+mk_defcnf fn fm =
+  let fm' = nenf fm in
+  let n = 1 + overatoms max_varindex fm' 0 in
+  let (fm'',defs,_) = fn (fm',Map.empty,n) in
+  let (deflist {- :: [pf]-}) = Map.elems defs in
+  Set.unions (simpcnf fm'' : map simpcnf deflist)
+
+defcnf1 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf1 fm = cnf (mk_defcnf maincnf fm :: Set.Set (Set.Set lit))
+
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf1 <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version tweaked to exploit initial structure.                             
+-- ------------------------------------------------------------------------- 
+
+subcnf :: (PropositionalFormula pf atom, NumAtom atom) =>
+          ((pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int))
+       -> (pf -> pf -> pf)
+       -> pf
+       -> pf
+       -> (pf, Map.Map pf pf, Int)
+       -> (pf, Map.Map pf pf, Int)
+subcnf sfn op p q (_fm,defs,n) =
+  let (fm1,defs1,n1) = sfn (p,defs,n) in
+  let (fm2,defs2,n2) = sfn (q,defs1,n1) in
+  (op fm1 fm2, defs2, n2)
+
+orcnf :: (NumAtom atom, PropositionalFormula pf atom) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+orcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:|:) q) = subcnf orcnf (.|.) p q trip
+      co _ = maincnf trip
+
+andcnf :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> orcnf trip) (\ _ -> orcnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf (.&.) p q trip
+      co _ = orcnf trip
+
+defcnfs :: (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+defcnfs fm = mk_defcnf andcnf fm
+
+defcnf2 :: forall pf lit atom.(PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf2 fm = cnf (defcnfs fm :: Set.Set (Set.Set lit))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+{-
+START_INTERACTIVE;;
+defcnf <<(p \/ (q /\ ~r)) /\ s>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Version that guarantees 3-CNF.                                            
+-- ------------------------------------------------------------------------- 
+
+andcnf3 :: (PropositionalFormula pf atom, NumAtom atom, Ord pf) => (pf, Map.Map pf pf, Int) -> (pf, Map.Map pf pf, Int)
+andcnf3 trip@(fm,_defs,_n) =
+    foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
+    where
+      co (BinOp p (:&:) q) = subcnf andcnf3 (.&.) p q trip
+      co _ = maincnf trip
+
+defcnf3 :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, NumAtom atom, Ord lit) => pf -> pf
+defcnf3 fm = cnf (mk_defcnf andcnf3 fm :: Set.Set (Set.Set lit))
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Equal.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Equal.hs
@@ -0,0 +1,322 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, RankNTypes, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Equal
+where
+
+import Data.Logic.Classes.Arity (Arity(..))
+import Data.Logic.Classes.Combine ((⇒), (∧))
+import Data.Logic.Classes.Constants (Constants(fromBool))
+import Data.Logic.Classes.Equals ((.=.), applyEq, AtomEq(..), funcsAtomEq, PredicateName(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), (∀))
+import Data.Logic.Classes.Formula (Formula(atomic, foldAtoms))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (atom_union)
+import Data.Logic.Harrison.Lib ((∅))
+import qualified Data.Set as Set (delete, empty, fold, fromList, member, Set, singleton, toList, union)
+import Data.String (IsString(fromString))
+
+-- is_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Bool
+-- is_eq = foldFirstOrder (\ _ _ _ -> False) (\ _ -> False) (\ _ -> False) (foldAtomEq (\ _ _ -> False) (\ _ -> False) (\ _ _ -> True))
+-- 
+-- mk_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => term -> term -> fof
+-- mk_eq = (.=.)
+-- 
+-- dest_eq :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing (term, term)
+-- dest_eq fm =
+--     foldFirstOrder (\ _ _ _ -> err) (\ _ -> err) (\ _ -> err) at fm
+--     where
+--       at = foldAtomEq (\ _ _ -> err) (\ _ -> err) (\ s t -> Success (s, t))
+--       err = Failure ["dest_eq: not an equation"]
+-- 
+-- lhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- lhs eq = dest_eq eq >>= return . fst
+-- rhs :: (FirstOrderFormula fof atom v, AtomEq atom p term) => fof -> Failing term
+-- rhs eq = dest_eq eq >>= return . snd
+
+-- ------------------------------------------------------------------------- 
+-- The set of predicates in a formula.                                       
+-- ------------------------------------------------------------------------- 
+
+predicates :: forall formula atom term v p. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) => formula -> Set.Set (PredicateName p)
+predicates fm =
+    atom_union pair fm
+    where -- pair :: atom -> Set.Set (p, Int)
+          pair = foldAtomEq (\ p a -> Set.singleton (Named p (maybe (length a)
+                                                                    (\ n -> if n /= length a then n else error "arity mismatch")
+                                                                    (arity p))))
+                            (\ x -> Set.singleton (Named (fromBool x) 0))
+                            (\ _ _ -> Set.singleton Equals)
+
+{-
+-- | Traverse a formula and pass all (predicates, arity) pairs to a function.
+-- To collect
+foldPredicates :: forall formula atom term v p r. (FirstOrderFormula formula atom v, AtomEq atom p term, Ord p) =>
+                  (PredicateName p -> Maybe Int -> r -> r) -> formula -> r -> r
+foldPredicates f fm acc =
+    foldFirstOrder qu co tf at fm
+    where
+      fold = foldPredicates f
+      qu _ _ p = fold p acc
+      co (BinOp l _ r) = fold r (fold l acc)
+      co ((:~:) p) = fold p acc
+      tf x = fold (fromBool x) acc
+      at = foldAtomEq ap tf eq
+      ap p _ = f (Name p) (arity p) acc
+      eq _ _ = f Equals (Just 2) acc
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Code to generate equality axioms for functions.                           
+-- ------------------------------------------------------------------------- 
+
+function_congruence :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f) =>
+                       (f, Int) -> Set.Set fof
+function_congruence (_,0) = (∅)
+function_congruence (f,n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x :: [v]
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y :: [v]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = fApp f args_x .=. fApp f args_y
+  
+-- ------------------------------------------------------------------------- 
+-- And for predicates.                                                       
+-- ------------------------------------------------------------------------- 
+
+predicate_congruence :: (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord p) =>
+                        PredicateName p -> Set.Set fof
+predicate_congruence Equals = Set.empty
+predicate_congruence (Named _ 0) = Set.empty
+predicate_congruence (Named p n) =
+    Set.singleton (foldr (∀) (ant ⇒ con) (argnames_x ++ argnames_y))
+    where
+      argnames_x = map (\ m -> fromString ("x" ++ show m)) [1..n]
+      argnames_y = map (\ m -> fromString ("y" ++ show m)) [1..n]
+      args_x = map vt argnames_x
+      args_y = map vt argnames_y
+      ant = foldr1 (∧) (map (uncurry (.=.)) (zip args_x args_y))
+      con = atomic (applyEq p args_x) ⇒ atomic (applyEq p args_y)
+
+-- ------------------------------------------------------------------------- 
+-- Hence implement logic with equality just by adding equality "axioms".     
+-- ------------------------------------------------------------------------- 
+
+equivalence_axioms :: forall fof atom term v p f. (FirstOrderFormula fof atom v, AtomEq atom p term, Term term v f, Ord fof) => Set.Set fof
+equivalence_axioms =
+    Set.fromList
+    [(∀) "x" (x .=. x),
+     (∀) "x" ((∀) "y" ((∀) "z" (x .=. y ∧ x .=. z ⇒ y .=. z)))]
+    where
+      x :: term
+      x = vt (fromString "x")
+      y :: term
+      y = vt (fromString "y")
+      z :: term
+      z = vt (fromString "z")
+
+equalitize :: forall formula atom term v p f. (FirstOrderFormula formula atom v, Formula formula atom, AtomEq atom p term, Ord p, Show p, Term term v f, Ord formula, Ord f) =>
+              formula -> formula
+equalitize fm =
+    if not (Set.member Equals allpreds)
+    then fm
+    else foldr1 (∧) (Set.toList axioms) ⇒ fm
+    where
+      axioms = Set.fold (Set.union . function_congruence) (Set.fold (Set.union . predicate_congruence) equivalence_axioms preds) (functions' funcsAtomEq' fm)
+      funcsAtomEq' :: atom -> Set.Set (f, Int)
+      funcsAtomEq' = funcsAtomEq
+      allpreds = predicates fm
+      preds = Set.delete Equals allpreds
+
+functions' :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions' fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- Other variants not mentioned in book.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+{- ************
+
+(meson ** equalitize)
+ <<(forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x * 1 = x) /\
+   (forall x. x * x = 1)
+   ==> forall x y. x * y  = y * x>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With symmetry at leaves and one-sided congruences (Size = 16, 54659 s).   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x. x = x) /\
+   (forall x y z. x * (y * z) = (x * y) * z) /\
+   (forall x y z. =((x * y) * z,x * (y * z))) /\
+   (forall x. 1 * x = x) /\
+   (forall x. x = 1 * x) /\
+   (forall x. i(x) * x = 1) /\
+   (forall x. 1 = i(x) * x) /\
+   (forall x y. x = y ==> i(x) = i(y)) /\
+   (forall x y z. x = y ==> x * z = y * z) /\
+   (forall x y z. x = y ==> z * x = z * y) /\
+   (forall x y z. x = y /\ y = z ==> x = z)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Newer version of stratified equalities.                                   
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cchain(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cchain(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall x x' u. x = x' /\ achain(i(x'),u) ==> cchain(i(x),u)) /\
+   (forall x x' y y' u.
+        x = x' /\ y = y' /\ achain(x' * y',u) ==> cchain(x * y,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+let fm =
+ <<(forall x y z. axiom(x * (y * z),(x * y) * z)) /\
+   (forall x y z. axiom((x * y) * z,x * (y * z)) /\
+   (forall x. axiom(1 * x,x)) /\
+   (forall x. axiom(x,1 * x)) /\
+   (forall x. axiom(i(x) * x,1)) /\
+   (forall x. axiom(1,i(x) * x)) /\
+   (forall x x'. x = x' ==> cong(i(x),i(x'))) /\
+   (forall x x' y y'. x = x' /\ y = y' ==> cong(x * y,x' * y'))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t)
+   ==> forall x. x * i(x) = 1>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Showing congruence closure.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm = equalitize
+ <<forall c. f(f(f(f(f(c))))) = c /\ f(f(f(c))) = c ==> f(c) = c>>;;
+
+time meson fm;;
+
+let fm =
+ <<axiom(f(f(f(f(f(c))))),c) /\
+   axiom(c,f(f(f(f(f(c)))))) /\
+   axiom(f(f(f(c))),c) /\
+   axiom(c,f(f(f(c)))) /\
+   (forall s t. axiom(s,t) ==> achain(s,t)) /\
+   (forall s t. cong(s,t) ==> cchain(s,t)) /\
+   (forall s t u. axiom(s,t) /\ (t = u) ==> achain(s,u)) /\
+   (forall s t u. cong(s,t) /\ achain(t,u) ==> cchain(s,u)) /\
+   (forall s t. cchain(s,t) ==> s = t) /\
+   (forall s t. achain(s,t) ==> s = t) /\
+   (forall t. t = t) /\
+   (forall x y. x = y ==> cong(f(x),f(y)))
+   ==> f(c) = c>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- With stratified equalities.                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x. eqA (x,x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqT (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y z. eqA (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqA (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqC (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqA (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqC (y,z) ==> eqC (w * y,x * z)) /\
+   (forall w x y z. eqT (w,x) /\ eqT (y,z) ==> eqC (w * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- With transitivity chains...                                               
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eqA (x * (y * z),(x * y) * z)) /\
+   (forall x y z. eqA ((x * y) * z)) /\
+   (forall x. eqA (1 * x,x)) /\
+   (forall x. eqA (x,1 * x)) /\
+   (forall x. eqA (i(x) * x,1)) /\
+   (forall x. eqA (1,i(x) * x)) /\
+   (forall x y. eqA (x,y) ==> eqC (i(x),i(y))) /\
+   (forall x y. eqC (x,y) ==> eqC (i(x),i(y))) /\
+   (forall w x y. eqA (w,x) ==> eqC (w * y,x * y)) /\
+   (forall w x y. eqC (w,x) ==> eqC (w * y,x * y)) /\
+   (forall x y z. eqA (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqC (y,z) ==> eqC (x * y,x * z)) /\
+   (forall x y z. eqA (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqA (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqC (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqA (x,y) /\ eqT (y,z) ==> eqT (x,z)) /\
+   (forall x y z. eqC (x,y) /\ eqT (y,z) ==> eqT (x,z))
+   ==> forall x. eqT (x * i(x),1) \/ eqC (x * i(x),1)>>;;
+
+time meson fm;;
+
+-- ------------------------------------------------------------------------- 
+-- Enforce canonicity (proof size = 20).                                     
+-- ------------------------------------------------------------------------- 
+
+let fm =
+ <<(forall x y z. eq1(x * (y * z),(x * y) * z)) /\
+   (forall x y z. eq1((x * y) * z,x * (y * z))) /\
+   (forall x. eq1(1 * x,x)) /\
+   (forall x. eq1(x,1 * x)) /\
+   (forall x. eq1(i(x) * x,1)) /\
+   (forall x. eq1(1,i(x) * x)) /\
+   (forall x y z. eq1(x,y) ==> eq1(x * z,y * z)) /\
+   (forall x y z. eq1(x,y) ==> eq1(z * x,z * y)) /\
+   (forall x y z. eq1(x,y) /\ eq2(y,z) ==> eq2(x,z)) /\
+   (forall x y. eq1(x,y) ==> eq2(x,y))
+   ==> forall x. eq2(x,i(x))>>;;
+
+time meson fm;;
+
+***************** -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/FOL.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/FOL.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.FOL
+    ( eval
+    , list_disj
+    , list_conj
+    , var
+    , fv
+    -- , fv'
+    , subst
+    -- , subst'
+    , generalize
+    ) where
+
+import Data.Logic.Classes.Apply (Apply(..), apply)
+import Data.Logic.Classes.Atom (Atom(allVariables, substitute))
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Constants (Constants (fromBool), true, false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term(vt), fvt)
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Formulas.FirstOrder (on_atoms)
+import Data.Logic.Harrison.Lib ((|->), setAny)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set
+import Prelude hiding (pred)
+
+-- =========================================================================
+-- Basic stuff for first order logic.                                       
+--                                                                          
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.) 
+-- =========================================================================
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: FirstOrderFormula formula atom v => formula -> (atom -> Bool) -> Bool
+eval fm v =
+    foldFirstOrder qu co id at fm
+    where
+      qu _ _ p = eval p v
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at = v
+
+list_conj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: (Constants formula, Combinable formula) => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (FirstOrderFormula formula atom v, Ord formula) =>
+          Set.Set formula -> (atom -> Bool) -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+-- -------------------------------------------------------------------------
+-- Special case of applying a subfunction to the top *terms*.               
+-- -------------------------------------------------------------------------
+
+on_formula :: forall fol atom term v p. (FirstOrderFormula fol atom v, Apply atom p term) => (term -> term) -> fol -> fol
+on_formula f = on_atoms (foldApply (\ p ts -> atomic (apply p (map f ts) :: atom)) fromBool)
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of terms.                                                         
+-- ------------------------------------------------------------------------- 
+
+{-
+let is_const_name s = forall numeric (explode s) or s = "nil";;
+
+let rec parse_atomic_term vs inp =
+  match inp with
+    [] -> failwith "term expected"
+  | "("::rest -> parse_bracketed (parse_term vs) ")" rest
+  | "-"::rest -> papply (fun t -> Fn("-",[t])) (parse_atomic_term vs rest)
+  | f::"("::")"::rest -> Fn(f,[]),rest
+  | f::"("::rest ->
+      papply (fun args -> Fn(f,args))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | a::rest ->
+      (if is_const_name a & not(mem a vs) then Fn(a,[]) else Var a),rest
+
+and parse_term vs inp =
+  parse_right_infix "::" (fun (e1,e2) -> Fn("::",[e1;e2]))
+    (parse_right_infix "+" (fun (e1,e2) -> Fn("+",[e1;e2]))
+       (parse_left_infix "-" (fun (e1,e2) -> Fn("-",[e1;e2]))
+          (parse_right_infix "*" (fun (e1,e2) -> Fn("*",[e1;e2]))
+             (parse_left_infix "/" (fun (e1,e2) -> Fn("/",[e1;e2]))
+                (parse_left_infix "^" (fun (e1,e2) -> Fn("^",[e1;e2]))
+                   (parse_atomic_term vs)))))) inp;;
+
+let parset = make_parser (parse_term []);;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas.                                                      
+-- ------------------------------------------------------------------------- 
+
+let parse_infix_atom vs inp =       
+  let tm,rest = parse_term vs inp in
+  if exists (nextin rest) ["="; "<"; "<="; ">"; ">="] then                     
+        papply (fun tm' -> Atom(R(hd rest,[tm;tm'])))                          
+               (parse_term vs (tl rest))                                       
+  else failwith "";;
+                                                               
+let parse_atom vs inp =
+  try parse_infix_atom vs inp with Failure _ ->                                
+  match inp with                                                               
+  | p::"("::")"::rest -> Atom(R(p,[])),rest                                    
+  | p::"("::rest ->
+      papply (fun args -> Atom(R(p,args)))
+             (parse_bracketed (parse_list "," (parse_term vs)) ")" rest)
+  | p::rest when p <> "(" -> Atom(R(p,[])),rest
+  | _ -> failwith "parse_atom";;
+                                                                               
+let parse = make_parser                                                        
+  (parse_formula (parse_infix_atom,parse_atom) []);;              
+
+-- ------------------------------------------------------------------------- 
+-- Set up parsing of quotations.                                             
+-- ------------------------------------------------------------------------- 
+
+let default_parser = parse;;
+
+let secondary_parser = parset;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printing of terms.                                                        
+-- ------------------------------------------------------------------------- 
+{-
+let rec print_term prec fm =
+  match fm with
+    Var x -> print_string x
+  | Fn("^",[tm1;tm2]) -> print_infix_term true prec 24 "^" tm1 tm2
+  | Fn("/",[tm1;tm2]) -> print_infix_term true prec 22 " /" tm1 tm2
+  | Fn("*",[tm1;tm2]) -> print_infix_term false prec 20 " *" tm1 tm2
+  | Fn("-",[tm1;tm2]) -> print_infix_term true prec 18 " -" tm1 tm2
+  | Fn("+",[tm1;tm2]) -> print_infix_term false prec 16 " +" tm1 tm2
+  | Fn("::",[tm1;tm2]) -> print_infix_term false prec 14 "::" tm1 tm2
+  | Fn(f,args) -> print_fargs f args
+
+and print_fargs f args =
+  print_string f;
+  if args = [] then () else
+   (print_string "(";
+    open_box 0;
+    print_term 0 (hd args); print_break 0 0;
+    do_list (fun t -> print_string ","; print_break 0 0; print_term 0 t)
+            (tl args);
+    close_box();
+    print_string ")")
+
+and print_infix_term isleft oldprec newprec sym p q =
+  if oldprec > newprec then (print_string "("; open_box 0) else ();
+  print_term (if isleft then newprec else newprec+1) p;
+  print_string sym;
+  print_break (if String.sub sym 0 1 = " " then 1 else 0) 0;
+  print_term (if isleft then newprec+1 else newprec) q;
+  if oldprec > newprec then (close_box(); print_string ")") else ();;
+
+let printert tm =
+  open_box 0; print_string "<<|";
+  open_box 0; print_term 0 tm; close_box();
+  print_string "|>>"; close_box();;
+
+#install_printer printert;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas.                                                     
+-- ------------------------------------------------------------------------- 
+
+let print_atom prec (R(p,args)) =
+  if mem p ["="; "<"; "<="; ">"; ">="] & length args = 2
+  then print_infix_term false 12 12 (" "^p) (el 0 args) (el 1 args)
+  else print_fargs p args;;
+
+let print_fol_formula = print_qformula print_atom;;
+
+#install_printer print_fol_formula;;
+
+-- ------------------------------------------------------------------------- 
+-- Examples in the main text.                                                
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+<<forall x y. exists z. x < z /\ y < z>>;;
+
+<<~(forall x. P(x)) <=> exists y. ~P(y)>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Free variables in terms and formulas.                                     
+-- ------------------------------------------------------------------------- 
+
+-- | Return all variables occurring in a formula.
+var :: forall formula atom term v.
+       (FirstOrderFormula formula atom v,
+        Atom atom term v) => formula -> Set.Set v
+var fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.insert x (var p)
+      co ((:~:) p) = var p
+      co (BinOp p _ q) = Set.union (var p) (var q)
+      tf _ = Set.empty
+      at :: atom -> Set.Set v
+      at = allVariables
+
+-- | Return the variables that occur free in a formula.
+fv :: forall formula atom term v.
+      (FirstOrderFormula formula atom v,
+       Atom atom term v) => formula -> Set.Set v
+fv fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = Set.delete x (fv p)
+      co ((:~:) p) = fv p
+      co (BinOp p _ q) = Set.union (fv p) (fv q)
+      tf _ = Set.empty
+      at = allVariables
+
+-- | Return the variables in a propositional formula.
+fv' :: forall formula atom term v. (PropositionalFormula formula atom, Atom atom term v, Ord v) => formula -> Set.Set v
+fv' fm =
+    foldPropositional co tf allVariables fm
+    where
+      co ((:~:) p) = fv' p
+      co (BinOp p _ q) = Set.union (fv' p) (fv' q)
+      tf _ = Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Universal closure of a formula.                                           
+-- ------------------------------------------------------------------------- 
+
+generalize :: (FirstOrderFormula formula atom v, Atom atom term v) => formula -> formula
+generalize fm = Set.fold for_all fm (fv fm)
+
+-- ------------------------------------------------------------------------- 
+-- Substitution in formulas, with variable renaming.                         
+-- ------------------------------------------------------------------------- 
+
+subst :: (FirstOrderFormula formula atom v,
+          Term term v f,
+          Atom atom term v) =>
+         Map.Map v term -> formula -> formula
+subst env fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x p = quant op x' (subst ((x |-> vt x') env) p)
+          where
+            x' = if setAny (\ y -> Set.member x (fvt (fromMaybe (vt y) (Map.lookup y env)))) (Set.delete x (fv p))
+                 then variant x (fv (subst (Map.delete x env) p))
+                 else x
+      co ((:~:) p) = ((.~.) (subst env p))
+      co (BinOp p op q) = binop (subst env p) op (subst env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+subst' :: (PropositionalFormula formula atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          Map.Map v term -> formula -> formula
+subst' env fm =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = ((.~.) (subst' env p))
+      co (BinOp p op q) = binop (subst' env p) op (subst' env q)
+      tf = fromBool
+      at = atomic . substitute env
+
+{-
+-- |Replace each free occurrence of variable old with term new.
+substitute :: forall formula atom term v f. (FirstOrderFormula formula atom v, Term term v f) => v -> term -> (atom -> formula) -> formula -> formula
+substitute old new atom formula =
+    foldTerm (\ new' -> if old == new' then formula else substitute' formula)
+             (\ _ _ -> substitute' formula)
+             new
+    where
+      substitute' =
+          foldFirstOrder -- If the old variable appears in a quantifier
+                -- we can stop doing the substitution.
+                (\ q v f' -> quant q v (if old == v then f' else substitute' f'))
+                (\ cm -> case cm of
+                           ((:~:) f') -> combine ((:~:) (substitute' f'))
+                           (BinOp f1 op f2) -> combine (BinOp (substitute' f1) op (substitute' f2)))
+                fromBool
+                atom
+-}
+{-
+    substitute old new atom formula
+    where 
+      atom = foldAtomEq (\ p ts -> pApp p (map st ts)) fromBool (\ t1 t2 -> st t1 .=. st t2)
+      st :: term -> term
+      st t = foldTerm sv (\ func ts -> fApp func (map st ts)) t
+      sv v = if v == old then new else vt v
+-}
+
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,211 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.FirstOrder
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), quant)
+import Data.Logic.Classes.Negate ((.~.))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: FirstOrderFormula formula atom v => formula -> formula
+antecedent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = err
+      err = error "antecedent"
+
+consequent :: FirstOrderFormula formula atom v => formula -> formula
+consequent formula =
+    foldFirstOrder (\ _ _ _ -> err) c (\ _ -> err) (\ _ -> err) formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = err
+      err = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
+on_atoms f fm =
+    foldFirstOrder qu co tf at fm
+    where 
+      qu op v fm' = quant op v (on_atoms f fm')
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at = f
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: FirstOrderFormula formula atom v => (atom -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldFirstOrder qu co tf pr fm
+    where
+      qu _ _ p = over_atoms f p b
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      pr atom = f atom b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (FirstOrderFormula formula atom v, Ord b) => (atom -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,208 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Formulas.Propositional
+    ( antecedent
+    , consequent
+    , on_atoms
+    , over_atoms
+    , atom_union
+    ) where
+
+import qualified Data.Set as Set
+--import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.Combine (Combination(..), BinOp(..), binop)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+
+-- ------------------------------------------------------------------------- 
+-- General parsing of iterated infixes.                                      
+-- ------------------------------------------------------------------------- 
+
+{-
+let rec parse_ginfix opsym opupdate sof subparser inp =
+  let e1,inp1 = subparser inp in
+  if inp1 <> [] & hd inp1 = opsym then
+     parse_ginfix opsym opupdate (opupdate sof e1) subparser (tl inp1)
+  else sof e1,inp1;;
+
+let parse_left_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> opcon(f e1,e2)) (fun x -> x);;
+
+let parse_right_infix opsym opcon =
+  parse_ginfix opsym (fun f e1 e2 -> f(opcon(e1,e2))) (fun x -> x);;
+
+let parse_list opsym =
+  parse_ginfix opsym (fun f e1 e2 -> (f e1)@[e2]) (fun x -> [x]);;
+
+-- ------------------------------------------------------------------------- 
+-- Other general parsing combinators.                                        
+-- ------------------------------------------------------------------------- 
+
+let papply f (ast,rest) = (f ast,rest);;
+
+let nextin inp tok = inp <> [] & hd inp = tok;;
+
+let parse_bracketed subparser cbra inp =
+  let ast,rest = subparser inp in
+  if nextin rest cbra then ast,tl rest
+  else failwith "Closing bracket expected";;
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of formulas, parametrized by atom parser "pfn".                   
+-- ------------------------------------------------------------------------- 
+
+let rec parse_atomic_formula (ifn,afn) vs inp =
+  match inp with
+    [] -> failwith "formula expected"
+  | "false"::rest -> False,rest
+  | "true"::rest -> True,rest
+  | "("::rest -> (try ifn vs inp with Failure _ ->
+                  parse_bracketed (parse_formula (ifn,afn) vs) ")" rest)
+  | "~"::rest -> papply (fun p -> Not p)
+                        (parse_atomic_formula (ifn,afn) vs rest)
+  | "forall"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Forall(x,p)) x rest
+  | "exists"::x::rest ->
+        parse_quant (ifn,afn) (x::vs) (fun (x,p) -> Exists(x,p)) x rest
+  | _ -> afn vs inp
+
+and parse_quant (ifn,afn) vs qcon x inp =
+   match inp with
+     [] -> failwith "Body of quantified term expected"
+   | y::rest ->
+        papply (fun fm -> qcon(x,fm))
+               (if y = "." then parse_formula (ifn,afn) vs rest
+                else parse_quant (ifn,afn) (y::vs) qcon y rest)
+
+and parse_formula (ifn,afn) vs inp =
+   parse_right_infix "<=>" (fun (p,q) -> Iff(p,q))
+     (parse_right_infix "==>" (fun (p,q) -> Imp(p,q))
+         (parse_right_infix "\\/" (fun (p,q) -> Or(p,q))
+             (parse_right_infix "/\\" (fun (p,q) -> And(p,q))
+                  (parse_atomic_formula (ifn,afn) vs)))) inp;;
+
+-- ------------------------------------------------------------------------- 
+-- Printing of formulas, parametrized by atom printer.                       
+-- ------------------------------------------------------------------------- 
+
+let bracket p n f x y =
+  (if p then print_string "(" else ());
+  open_box n; f x y; close_box();
+  (if p then print_string ")" else ());;
+
+let rec strip_quant fm =
+  match fm with
+    Forall(x,(Forall(y,p) as yp)) | Exists(x,(Exists(y,p) as yp)) ->
+        let xs,q = strip_quant yp in x::xs,q
+  |  Forall(x,p) | Exists(x,p) -> [x],p
+  | _ -> [],fm;;
+
+let print_formula pfn =
+  let rec print_formula pr fm =
+    match fm with
+      False -> print_string "false"
+    | True -> print_string "true"
+    | Atom(pargs) -> pfn pr pargs
+    | Not(p) -> bracket (pr > 10) 1 (print_prefix 10) "~" p
+    | And(p,q) -> bracket (pr > 8) 0 (print_infix 8 "/\\") p q
+    | Or(p,q) ->  bracket (pr > 6) 0 (print_infix  6 "\\/") p q
+    | Imp(p,q) ->  bracket (pr > 4) 0 (print_infix 4 "==>") p q
+    | Iff(p,q) ->  bracket (pr > 2) 0 (print_infix 2 "<=>") p q
+    | Forall(x,p) -> bracket (pr > 0) 2 print_qnt "forall" (strip_quant fm)
+    | Exists(x,p) -> bracket (pr > 0) 2 print_qnt "exists" (strip_quant fm)
+  and print_qnt qname (bvs,bod) =
+    print_string qname;
+    do_list (fun v -> print_string " "; print_string v) bvs;
+    print_string "."; print_space(); open_box 0;
+    print_formula 0 bod;
+    close_box()
+  and print_prefix newpr sym p =
+   print_string sym; print_formula (newpr+1) p
+  and print_infix newpr sym p q =
+    print_formula (newpr+1) p;
+    print_string(" "^sym); print_space();
+    print_formula newpr q in
+  print_formula 0;;
+
+let print_qformula pfn fm =
+  open_box 0; print_string "<<";
+  open_box 0; print_formula pfn fm; close_box();
+  print_string ">>"; close_box();;
+
+-- ------------------------------------------------------------------------- 
+-- OCaml won't let us use the constructors.                                  
+-- ------------------------------------------------------------------------- 
+
+let mk_and p q = And(p,q) and mk_or p q = Or(p,q)
+and mk_imp p q = Imp(p,q) and mk_iff p q = Iff(p,q)
+and mk_forall x p = Forall(x,p) and mk_exists x p = Exists(x,p);;
+
+-- ------------------------------------------------------------------------- 
+-- Destructors.                                                              
+-- ------------------------------------------------------------------------- 
+
+let dest_iff fm =
+  match fm with Iff(p,q) -> (p,q) | _ -> failwith "dest_iff";;
+
+let dest_and fm =
+  match fm with And(p,q) -> (p,q) | _ -> failwith "dest_and";;
+
+let rec conjuncts fm =
+  match fm with And(p,q) -> conjuncts p @ conjuncts q | _ -> [fm];;
+
+let dest_or fm =
+  match fm with Or(p,q) -> (p,q) | _ -> failwith "dest_or";;
+
+let rec disjuncts fm =
+  match fm with Or(p,q) -> disjuncts p @ disjuncts q | _ -> [fm];;
+
+let dest_imp fm =
+  match fm with Imp(p,q) -> (p,q) | _ -> failwith "dest_imp";;
+-}
+
+antecedent :: PropositionalFormula formula atomic => formula -> formula
+antecedent formula =
+    foldPropositional c (error "antecedent") (error "antecedent") formula
+    where
+      c (BinOp p (:=>:) _) = p
+      c _ = error "antecedent"
+
+consequent :: PropositionalFormula formula atomic => formula -> formula
+consequent formula =
+    foldPropositional c (error "consequent") (error "consequent") formula
+    where
+      c (BinOp _ (:=>:) q) = q
+      c _ = error "consequent"
+
+-- ------------------------------------------------------------------------- 
+-- Apply a function to the atoms, otherwise keeping structure.               
+-- ------------------------------------------------------------------------- 
+
+on_atoms :: PropositionalFormula formula atomic => (atomic -> formula) -> formula -> formula
+on_atoms f fm =
+    foldPropositional co tf at fm
+    where 
+      co ((:~:) fm') = (.~.) (on_atoms f fm')
+      co (BinOp f1 op f2) = binop (on_atoms f f1) op (on_atoms f f2)
+      tf _ = fm
+      at x = f x
+
+-- ------------------------------------------------------------------------- 
+-- Formula analog of list iterator "itlist".                                 
+-- ------------------------------------------------------------------------- 
+
+over_atoms :: (PropositionalFormula formula atomic) => (atomic -> b -> b) -> formula -> b -> b
+over_atoms f fm b =
+    foldPropositional co tf at fm
+    where
+      co ((:~:) p) = over_atoms f p b
+      co (BinOp p _ q) = over_atoms f p (over_atoms f q b)
+      tf _ = b
+      at x = f x b
+
+-- ------------------------------------------------------------------------- 
+-- Special case of a union of the results of a function over the atoms.      
+-- ------------------------------------------------------------------------- 
+
+atom_union :: (PropositionalFormula formula atomic, Ord b) => (atomic -> Set.Set b) -> formula -> Set.Set b
+atom_union f fm = over_atoms (\ h t -> Set.union (f h) t) fm Set.empty
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Herbrand.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Herbrand.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Herbrand.hs
@@ -0,0 +1,309 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Harrison.Herbrand where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom(substitute, freeVariables))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (fApp, Term)
+import Data.Logic.Harrison.DP (dpll)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib')
+import Data.Logic.Harrison.Normal (trivial)
+import Data.Logic.Harrison.Prop (eval, simpcnf, simpdnf)
+import Data.Logic.Harrison.Skolem (functions, runSkolem, skolemize)
+import qualified Data.Map as Map (fromList, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, insert, map, minView, null, partition, Set, singleton, size, toList, union)
+import Data.String (IsString(..))
+
+-- ========================================================================= 
+-- Relation between FOL and propositonal logic; Herbrand theorem.            
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Propositional valuation.                                                  
+-- ------------------------------------------------------------------------- 
+
+pholds :: (PropositionalFormula formula atom, Ord atom) => (Map.Map atom Bool) -> formula -> Bool
+pholds d fm = eval fm d
+
+-- ------------------------------------------------------------------------- 
+-- Get the constants for Herbrand base, adding nullary one if necessary.     
+-- ------------------------------------------------------------------------- 
+
+herbfuns :: forall pf atom term v f. (PropositionalFormula pf atom, Formula pf atom, Atom atom term v, Term term v f, IsString f, Ord f) =>
+            (atom -> Set.Set (f, Int))
+         -> pf
+         -> (Set.Set (f, Int), Set.Set (f, Int))
+herbfuns fa fm =
+  let (cns,fns) = Set.partition (\ (_,ar) -> ar == 0) (functions fa fm) in
+  if Set.null cns then (Set.singleton (fromString "c",0),fns) else (cns,fns)
+
+-- ------------------------------------------------------------------------- 
+-- Enumeration of ground terms and m-tuples, ordered by total fns.           
+-- ------------------------------------------------------------------------- 
+
+groundterms :: forall term v f. (Term term v f) =>
+               Set.Set term -> Set.Set (f, Int) -> Int -> Set.Set term
+groundterms cntms _ 0 = cntms
+groundterms cntms funcs n =
+    Set.fold terms Set.empty funcs
+    where
+      terms (f,m) l = Set.union (Set.map (fApp f) (groundtuples cntms funcs (n - 1) m)) l
+
+groundtuples :: forall term v f. (Term term v f) =>
+                Set.Set term -> Set.Set (f, Int) -> Int -> Int -> Set.Set [term]
+groundtuples _ _ 0 0 = Set.singleton []
+groundtuples _ _ _ 0 = Set.empty
+groundtuples cntms funcs n m =
+    Set.fold tuples Set.empty (Set.fromList [0 .. n])
+    where 
+      tuples k l = Set.union (allpairs (:) (groundterms cntms funcs k) (groundtuples cntms funcs (n - k) (m - 1))) l
+
+-- ------------------------------------------------------------------------- 
+-- Iterate modifier "mfn" over ground terms till "tfn" fails.                
+-- ------------------------------------------------------------------------- 
+
+herbloop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v) =>
+            (Set.Set (Set.Set lit) -> (lit -> lit) -> Set.Set (Set.Set lit) -> Set.Set (Set.Set lit))
+         -> (Set.Set (Set.Set lit) -> Failing Bool)
+         -> Set.Set (Set.Set lit)
+         -> Set.Set term
+         -> Set.Set (f, Int)
+         -> [v]
+         -> Int
+         -> Set.Set (Set.Set lit)
+         -> Set.Set [term]
+         -> Set.Set [term]
+         -> Failing (Set.Set [term])
+herbloop mfn tfn fl0 cntms funcs fvs n fl tried tuples =
+{-
+  print_string(string_of_int(length tried) ++ " ground instances tried; " ++
+               string_of_int(length fl) ++ " items in list")
+  print_newline();
+-}
+  case Set.minView tuples of
+    Nothing ->
+          let newtups = groundtuples cntms funcs n (length fvs) in
+          herbloop mfn tfn fl0 cntms funcs fvs (n + 1) fl tried newtups
+    Just (tup, tups) ->
+        let fpf' = Map.fromList (zip fvs tup) in
+        let fl' = mfn fl0 (subst' fpf') fl in
+        case tfn fl' of
+          Failure msgs -> Failure msgs
+          Success x ->
+              if not x
+              then Success (Set.insert tup tried)
+              else herbloop mfn tfn fl0 cntms funcs fvs n fl' (Set.insert tup tried) tups
+
+subst' :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => Map.Map v term -> lit -> lit
+subst' env fm =
+    mapAtoms (atomic . substitute') fm
+    where substitute' :: atom -> atom
+          substitute' = substitute env
+
+-- ------------------------------------------------------------------------- 
+-- Hence a simple Gilmore-type procedure.                                    
+-- ------------------------------------------------------------------------- 
+
+gilmore_loop :: (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit)
+             -> Set.Set term
+             -> Set.Set (f, Int)
+             -> [v]
+             -> Int
+             -> Set.Set (Set.Set lit)
+             -> Set.Set [term]
+             -> Set.Set [term]
+             -> Failing (Set.Set [term])
+gilmore_loop =
+    herbloop mfn (Success . not . Set.null)
+    where
+      mfn djs0 ifn djs = Set.filter (not . trivial) (distrib' (Set.map (Set.map ifn) djs0) djs)
+
+gilmore :: forall fof pf atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            Atom atom term v,
+            IsString f,
+            Ord pf) =>
+           (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+gilmore fa fm =
+  let sfm = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union s (freeVariables a)) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+  gilmore_loop (simpdnf sfm :: Set.Set (Set.Set pf)) cntms funcs (fvs) 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- First example and a little tracing.                                       
+-- ------------------------------------------------------------------------- 
+{-
+test01 =
+    let fm = exists "x" (for_all "y" (pApp "p" [vt "x"] .=>. pApp "p" [vt "y"]))
+        sfm = skolemize ((.~.) fm) in
+    TestList [TestCase (assertEqual "gilmore 1" 2 (gilmore fm))]
+
+START_INTERACTIVE;;
+gilmore <<exists x. forall y. P(x) ==> P(y)>>;;
+
+let sfm = skolemize(Not <<exists x. forall y. P(x) ==> P(y)>>);;
+
+-- ------------------------------------------------------------------------- 
+-- Quick example.                                                            
+-- ------------------------------------------------------------------------- 
+
+let p24 = gilmore
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x))
+   ==> (exists x. P(x) /\ R(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Slightly less easy example.                                               
+-- ------------------------------------------------------------------------- 
+
+let p45 = gilmore
+ <<(forall x. P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))
+              ==> (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==> L(y)) /\
+                      (forall y. G(y) /\ H(x,y) ==> J(x,y)))
+   ==> (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+END_INTERACTIVE;;
+-}
+-- ------------------------------------------------------------------------- 
+-- Apparently intractable example.                                           
+-- ------------------------------------------------------------------------- 
+
+{-
+
+let p20 = gilmore
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+-}
+
+
+-- ------------------------------------------------------------------------- 
+-- The Davis-Putnam procedure for first order logic.                         
+-- ------------------------------------------------------------------------- 
+
+dp_mfn :: (Ord b, Ord a) =>
+          Set.Set (Set.Set a)
+       -> (a -> b)
+       -> Set.Set (Set.Set b)
+       -> Set.Set (Set.Set b)
+dp_mfn cjs0 ifn cjs = Set.union (Set.map (Set.map ifn) cjs0) cjs
+
+dp_loop :: forall lit atom v term f. (Literal lit atom, Term term v f, Atom atom term v, Ord lit) =>
+           Set.Set (Set.Set lit)
+        -> Set.Set term
+        -> Set.Set (f, Int)
+        -> [v]
+        -> Int
+        -> Set.Set (Set.Set lit)
+        -> Set.Set [term]
+        -> Set.Set [term]
+        -> Failing (Set.Set [term])
+dp_loop = herbloop dp_mfn dpll
+
+davisputnam :: forall fof atom term v lit f.
+               (FirstOrderFormula fof atom v,
+                PropositionalFormula lit atom,
+                Literal lit atom,
+                Term term v f,
+                Atom atom term v,
+                IsString f,
+                Ord lit) =>
+               (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam fa fm =
+  let (sfm :: lit) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+  let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+      (consts,funcs) = herbfuns fa sfm in
+  let cntms = Set.map (\ (c,_) -> fApp c [] :: term) consts in
+  dp_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+-- ------------------------------------------------------------------------- 
+-- Show how much better than the Gilmore procedure this can be.              
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+let p20 = davisputnam
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Try to cut out useless instantiations in final result.                    
+-- ------------------------------------------------------------------------- 
+
+dp_refine :: (Literal lit atom, Atom atom term v, Term term v f) =>
+             Set.Set (Set.Set lit) -> [v] -> Set.Set [term] -> Set.Set [term] -> Failing (Set.Set [term])
+dp_refine cjs0 fvs dknow need =
+    case Set.minView dknow of
+      Nothing -> Success need
+      Just (cl, dknow') ->
+          let mfn = dp_mfn cjs0 . subst' . Map.fromList . zip fvs in
+          dpll (Set.fold mfn Set.empty (Set.union need dknow')) >>= \ flag ->
+          if flag then return (Set.insert cl need) else return need >>=
+          dp_refine cjs0 fvs dknow'
+
+dp_refine_loop :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+                  Set.Set (Set.Set lit)
+               -> Set.Set term
+               -> Set.Set (f, Int)
+               -> [v]
+               -> Int
+               -> Set.Set (Set.Set lit)
+               -> Set.Set [term]
+               -> Set.Set [term]
+               -> Failing (Set.Set [term])
+dp_refine_loop cjs0 cntms funcs fvs n cjs tried tuples =
+    dp_loop cjs0 cntms funcs fvs n cjs tried tuples >>= \ tups ->
+    dp_refine cjs0 fvs tups Set.empty
+
+-- ------------------------------------------------------------------------- 
+-- Show how few of the instances we really need. Hence unification!          
+-- ------------------------------------------------------------------------- 
+
+davisputnam' :: forall fof atom term lit v f pf.
+                (FirstOrderFormula fof atom v,
+                 Literal lit atom,
+                 PropositionalFormula pf atom, -- Formula pf atom,
+                 Term term v f,
+                 Atom atom term v,
+                 IsString f) =>
+                (atom -> Set.Set (f, Int)) -> fof -> Failing Int
+davisputnam' fa fm =
+    let (sfm :: pf) = runSkolem (skolemize id ((.~.)(generalize fm))) in
+    let fvs = Set.toList (foldAtoms (\ s (a :: atom) -> Set.union (freeVariables a) s) Set.empty sfm)
+        (consts,funcs) = herbfuns fa sfm in
+    let cntms = Set.map (\ (c,_) -> fApp c []) consts in
+    dp_refine_loop (simpcnf sfm :: Set.Set (Set.Set lit)) cntms funcs fvs 0 Set.empty Set.empty Set.empty >>= return . Set.size
+
+{-
+START_INTERACTIVE;;
+let p36 = davisputnam'
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+                ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+   ==> (forall x. exists y. H(x,y))>>;;
+
+let p29 = davisputnam'
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/LICENSE.txt b/testdata/split-merge-merge-expected/Data/Logic/Harrison/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/LICENSE.txt
@@ -0,0 +1,35 @@
+IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+By downloading, copying, installing or using the software you agree
+to this license.  If you do not agree to this license, do not
+download, install, copy or use the software.
+
+Copyright (c) 2003-2007, John Harrison
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* The name of John Harrison may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Lib.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Lib.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Lib.hs
@@ -0,0 +1,846 @@
+{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}
+{-# OPTIONS_GHC -Wall -fno-warn-unused-binds #-}
+module Data.Logic.Harrison.Lib
+    ( tests
+    , setAny
+    , setAll
+    -- , itlist2
+    -- , itlist  -- same as foldr with last arguments flipped
+    , tryfind
+    , settryfind
+    -- , end_itlist -- same as foldr1
+    , (|=>)
+    , (|->)
+    , fpf
+    , defined
+    , apply
+    , exists
+    , tryApplyD
+    , allpairs
+    , distrib'
+    , image
+    , optimize
+    , minimize
+    , maximize
+    , optimize'
+    , minimize'
+    , maximize'
+    , can
+    , allsets
+    , allsubsets
+    , allnonemptysubsets
+    , mapfilter
+    , setmapfilter
+    , (∅)
+    ) where
+
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+import Data.Maybe
+import qualified Data.Set as Set
+import Test.HUnit (Test(TestCase, TestList, TestLabel), assertEqual)
+
+(∅) :: Set.Set a
+(∅) = Set.empty
+
+tests :: Test
+tests = TestLabel "Data.Logic.Harrison.Lib" $ TestList [test01]
+
+setAny :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAny f s = Set.member True (Set.map f s)
+
+setAll :: forall a. Ord a => (a -> Bool) -> Set.Set a -> Bool
+setAll f s = not (Set.member False (Set.map f s))
+
+{-
+(* ========================================================================= *)
+(* Misc library functions to set up a nice environment.                      *)
+(* ========================================================================= *)
+
+let identity x = x;;
+
+let ( ** ) = fun f g x -> f(g x);;
+
+(* ------------------------------------------------------------------------- *)
+(* GCD and LCM on arbitrary-precision numbers.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let gcd_num n1 n2 =
+  abs_num(num_of_big_int
+      (Big_int.gcd_big_int (big_int_of_num n1) (big_int_of_num n2)));;
+
+let lcm_num n1 n2 = abs_num(n1 */ n2) // gcd_num n1 n2;;
+
+(* ------------------------------------------------------------------------- *)
+(* A useful idiom for "non contradictory" etc.                               *)
+(* ------------------------------------------------------------------------- *)
+
+let non p x = not(p x);;
+
+(* ------------------------------------------------------------------------- *)
+(* Kind of assertion checking.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let check p x = if p(x) then x else failwith "check";;
+
+(* ------------------------------------------------------------------------- *)
+(* Repetition of a function.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec funpow n f x =
+  if n < 1 then x else funpow (n-1) f (f x);;
+-}
+-- let can f x = try f x; true with Failure _ -> false;;
+can :: (t -> Failing a) -> t -> Bool
+can f x = failing (const True) (const False) (f x)
+
+{-
+let rec repeat f x = try repeat f (f x) with Failure _ -> x;;
+
+(* ------------------------------------------------------------------------- *)
+(* Handy list operations.                                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec (--) = fun m n -> if m > n then [] else m::((m + 1) -- n);;
+
+let rec (---) = fun m n -> if m >/ n then [] else m::((m +/ Int 1) --- n);;
+
+let rec map2 f l1 l2 =
+  match (l1,l2) with
+    [],[] -> []
+  | (h1::t1),(h2::t2) -> let h = f h1 h2 in h::(map2 f t1 t2)
+  | _ -> failwith "map2: length mismatch";;
+
+let rev =
+  let rec rev_append acc l =
+    match l with
+      [] -> acc
+    | h::t -> rev_append (h::acc) t in
+  fun l -> rev_append [] l;;
+
+let hd l =
+  match l with
+   h::t -> h
+  | _ -> failwith "hd";;
+
+let tl l =
+  match l with
+   h::t -> t
+  | _ -> failwith "tl";;
+-}
+
+-- (^) = (++)
+
+itlist :: (a -> b -> b) -> [a] -> b -> b
+-- itlist f xs z = foldr f z xs
+itlist f xs z = foldr f z xs
+
+end_itlist :: (t -> t -> t) -> [t] -> t
+-- end_itlist = foldr1
+end_itlist = foldr1
+
+itlist2 :: (t -> t1 -> Failing t2 -> Failing t2) -> [t] -> [t1] -> Failing t2 -> Failing t2
+itlist2 f l1 l2 b =
+  case (l1,l2) of
+    ([],[]) -> b
+    (h1 : t1, h2 : t2) -> f h1 h2 (itlist2 f t1 t2 b)
+    _ -> Failure ["itlist2"]
+
+{-
+let rec zip l1 l2 =
+  match (l1,l2) with
+        ([],[]) -> []
+      | (h1::t1,h2::t2) -> (h1,h2)::(zip t1 t2)
+      | _ -> failwith "zip";;
+
+let rec forall p l =
+  match l with
+    [] -> true
+  | h::t -> p(h) & forall p t;;
+-}
+exists :: (a -> Bool) -> [a] -> Bool
+exists = any
+{-
+let partition p l =
+    itlist (fun a (yes,no) -> if p a then a::yes,no else yes,a::no) l ([],[]);;
+
+let filter p l = fst(partition p l);;
+
+let length =
+  let rec len k l =
+    if l = [] then k else len (k + 1) (tl l) in
+  fun l -> len 0 l;;
+
+let rec last l =
+  match l with
+    [x] -> x
+  | (h::t) -> last t
+  | [] -> failwith "last";;
+
+let rec butlast l =
+  match l with
+    [_] -> []
+  | (h::t) -> h::(butlast t)
+  | [] -> failwith "butlast";;
+
+let rec find p l =
+  match l with
+      [] -> failwith "find"
+    | (h::t) -> if p(h) then h else find p t;;
+
+let rec el n l =
+  if n = 0 then hd l else el (n - 1) (tl l);;
+
+let map f =
+  let rec mapf l =
+    match l with
+      [] -> []
+    | (x::t) -> let y = f x in y::(mapf t) in
+  mapf;;
+-}
+
+allpairs :: forall a b c. (Ord c) => (a -> b -> c) -> Set.Set a -> Set.Set b -> Set.Set c
+-- allpairs f xs ys = Set.fromList (concatMap (\ z -> map (f z) (Set.toList ys)) (Set.toList xs))
+allpairs f xs ys = Set.fold (\ x zs -> Set.fold (\ y zs' -> Set.insert (f x y) zs') zs ys) Set.empty xs
+
+distrib' :: Ord a => Set.Set (Set.Set a) -> Set.Set (Set.Set a) -> Set.Set (Set.Set a)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+test01 :: Test
+test01 = TestCase $ assertEqual "itlist2" expected input
+    where input = allpairs (,) (Set.fromList [1,2,3]) (Set.fromList [4,5,6])
+          expected = Set.fromList [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)] :: Set.Set (Int, Int)
+
+{-
+let rec distinctpairs l =
+  match l with
+   x::t -> itlist (fun y a -> (x,y) :: a) t (distinctpairs t)
+  | [] -> [];;
+
+let rec chop_list n l =
+  if n = 0 then [],l else
+  try let m,l' = chop_list (n-1) (tl l) in (hd l)::m,l'
+  with Failure _ -> failwith "chop_list";;
+
+let replicate n a = map (fun x -> a) (1--n);;
+
+let rec insertat i x l =
+  if i = 0 then x::l else
+  match l with
+    [] -> failwith "insertat: list too short for position to exist"
+  | h::t -> h::(insertat (i-1) x t);;
+
+let rec forall2 p l1 l2 =
+  match (l1,l2) with
+    [],[] -> true
+  | (h1::t1,h2::t2) -> p h1 h2 & forall2 p t1 t2
+  | _ -> false;;
+
+let index x =
+  let rec ind n l =
+    match l with
+      [] -> failwith "index"
+    | (h::t) -> if Pervasives.compare x h = 0 then n else ind (n + 1) t in
+  ind 0;;
+
+let rec unzip l =
+  match l with
+    [] -> [],[]
+  | (x,y)::t ->
+      let xs,ys = unzip t in x::xs,y::ys;;
+
+(* ------------------------------------------------------------------------- *)
+(* Whether the first of two items comes earlier in the list.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let rec earlier l x y =
+  match l with
+    h::t -> (Pervasives.compare h y <> 0) &
+            (Pervasives.compare h x = 0 or earlier t x y)
+  | [] -> false;;
+
+(* ------------------------------------------------------------------------- *)
+(* Application of (presumably imperative) function over a list.              *)
+(* ------------------------------------------------------------------------- *)
+
+let rec do_list f l =
+  match l with
+    [] -> ()
+  | h::t -> f(h); do_list f t;;
+
+(* ------------------------------------------------------------------------- *)
+(* Association lists.                                                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare x a = 0 then y else assoc a t
+  | [] -> failwith "find";;
+
+let rec rev_assoc a l =
+  match l with
+    (x,y)::t -> if Pervasives.compare y a = 0 then x else rev_assoc a t
+  | [] -> failwith "find";;
+
+(* ------------------------------------------------------------------------- *)
+(* Merging of sorted lists (maintaining repetitions).                        *)
+(* ------------------------------------------------------------------------- *)
+
+let rec merge ord l1 l2 =
+  match l1 with
+    [] -> l2
+  | h1::t1 -> match l2 with
+                [] -> l1
+              | h2::t2 -> if ord h1 h2 then h1::(merge ord t1 l2)
+                          else h2::(merge ord l1 t2);;
+
+(* ------------------------------------------------------------------------- *)
+(* Bottom-up mergesort.                                                      *)
+(* ------------------------------------------------------------------------- *)
+
+let sort ord =
+  let rec mergepairs l1 l2 =
+    match (l1,l2) with
+        ([s],[]) -> s
+      | (l,[]) -> mergepairs [] l
+      | (l,[s1]) -> mergepairs (s1::l) []
+      | (l,(s1::s2::ss)) -> mergepairs ((merge ord s1 s2)::l) ss in
+  fun l -> if l = [] then [] else mergepairs [] (map (fun x -> [x]) l);;
+
+(* ------------------------------------------------------------------------- *)
+(* Common measure predicates to use with "sort".                             *)
+(* ------------------------------------------------------------------------- *)
+
+let increasing f x y = Pervasives.compare (f x) (f y) < 0;;
+
+let decreasing f x y = Pervasives.compare (f x) (f y) > 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* Eliminate repetitions of adjacent elements, with and without counting.    *)
+(* ------------------------------------------------------------------------- *)
+
+let rec uniq l =
+  match l with
+    x::(y::_ as t) -> let t' = uniq t in
+                      if Pervasives.compare x y = 0 then t' else
+                      if t'==t then l else x::t'
+ | _ -> l;;
+
+let repetitions =
+  let rec repcount n l =
+    match l with
+      x::(y::_ as ys) -> if Pervasives.compare y x = 0 then repcount (n + 1) ys
+                  else (x,n)::(repcount 1 ys)
+    | [x] -> [x,n]
+    | [] -> failwith "repcount" in
+  fun l -> if l = [] then [] else repcount 1 l;;
+-}
+
+tryfind :: (t -> Failing a) -> [t] -> Failing a
+tryfind _ [] = Failure ["tryfind"]
+tryfind f l =
+    case l of
+      [] -> Failure ["tryfind"]
+      h : t -> failing (\ _ -> tryfind f t) Success (f h)
+
+settryfind :: (t -> Failing a) -> Set.Set t -> Failing a
+settryfind f l =
+    case Set.minView l of
+      Nothing -> Failure ["settryfind"]
+      Just (h, t) -> failing (\ _ -> settryfind f t) Success (f h)
+
+mapfilter :: (a -> Failing b) -> [a] -> [b]
+mapfilter f l = catMaybes (map (failing (const Nothing) Just . f) l) 
+    -- filter (failing (const False) (const True)) (map f l)
+
+setmapfilter :: Ord b => (a -> Failing b) -> Set.Set a -> Set.Set b
+setmapfilter f s = Set.fold (\ a r -> failing (const r) (`Set.insert` r) (f a)) Set.empty s
+
+-- -------------------------------------------------------------------------
+-- Find list member that maximizes or minimizes a function.                 
+-- -------------------------------------------------------------------------
+
+optimize :: forall a b. (b -> b -> Bool) -> (a -> b) -> [a] -> Maybe a
+optimize _ _ [] = Nothing
+optimize ord f l = Just (fst (foldr1 (\ p@(_,y) p'@(_,y') -> if ord y y' then p else p') (map (\ x -> (x,f x)) l)))
+
+maximize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+maximize f l = optimize (>) f l
+
+minimize :: forall a b. Ord b => (a -> b) -> [a] -> Maybe a
+minimize f l = optimize (<) f l
+
+optimize' :: forall a b. (b -> b -> Bool) -> (a -> b) -> Set.Set a -> Maybe a
+optimize' ord f s = optimize ord f (Set.toList s)
+
+maximize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+maximize' f s = optimize' (>) f s
+
+minimize' :: forall a b. Ord b => (a -> b) -> Set.Set a -> Maybe a
+minimize' f s = optimize' (<) f s
+
+-- -------------------------------------------------------------------------
+-- Set operations on ordered lists.                                         
+-- -------------------------------------------------------------------------
+{-
+let setify =
+  let rec canonical lis =
+     match lis with
+       x::(y::_ as rest) -> Pervasives.compare x y < 0 & canonical rest
+     | _ -> true in
+  fun l -> if canonical l then l
+           else uniq (sort (fun x y -> Pervasives.compare x y <= 0) l);;
+
+let union =
+  let rec union l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> l2
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(union t1 t2)
+          else if h1 < h2 then h1::(union t1 l2)
+          else h2::(union l1 t2) in
+  fun s1 s2 -> union (setify s1) (setify s2);;
+
+let intersect =
+  let rec intersect l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> []
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then h1::(intersect t1 t2)
+          else if h1 < h2 then intersect t1 l2
+          else intersect l1 t2 in
+  fun s1 s2 -> intersect (setify s1) (setify s2);;
+
+let subtract =
+  let rec subtract l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> []
+      | (l1,[]) -> l1
+      | ((h1::t1 as l1),(h2::t2 as l2)) ->
+          if h1 = h2 then subtract t1 t2
+          else if h1 < h2 then h1::(subtract t1 l2)
+          else subtract l1 t2 in
+  fun s1 s2 -> subtract (setify s1) (setify s2);;
+
+let subset,psubset =
+  let rec subset l1 l2 =
+    match (l1,l2) with
+        ([],l2) -> true
+      | (l1,[]) -> false
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then subset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2
+  and psubset l1 l2 =
+    match (l1,l2) with
+        (l1,[]) -> false
+      | ([],l2) -> true
+      | (h1::t1,h2::t2) ->
+          if h1 = h2 then psubset t1 t2
+          else if h1 < h2 then false
+          else subset l1 t2 in
+  (fun s1 s2 -> subset (setify s1) (setify s2)),
+  (fun s1 s2 -> psubset (setify s1) (setify s2));;
+
+let rec set_eq s1 s2 = (setify s1 = setify s2);;
+
+let insert x s = union [x] s;;
+-}
+
+image :: (Ord b, Ord a) => (a -> b) -> Set.Set a -> Set.Set b
+image f s = Set.map f s
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Union of a family of sets.                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let unions s = setify(itlist (@) s []);;
+
+(* ------------------------------------------------------------------------- *)
+(* List membership. This does *not* assume the list is a set.                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec mem x lis =
+  match lis with
+    [] -> false
+  | (h::t) -> Pervasives.compare x h = 0 or mem x t;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Finding all subsets or all subsets of a given size.                       
+-- ------------------------------------------------------------------------- 
+
+-- allsets :: Ord a => Int -> Set.Set a -> Set.Set (Set.Set a)
+allsets :: forall a b. (Num a, Eq a, Ord b) => a -> Set.Set b -> Set.Set (Set.Set b)
+allsets 0 _ = Set.singleton Set.empty
+allsets m l =
+    case Set.minView l of
+      Nothing -> Set.empty
+      Just (h, t) -> Set.union (Set.map (Set.insert h) (allsets (m - 1) t)) (allsets m t)
+
+allsubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allsubsets s =
+    maybe (Set.singleton Set.empty)
+          (\ (x, t) -> 
+               let res = allsubsets t in
+               Set.union res (Set.map (Set.insert x) res))
+          (Set.minView s)
+
+
+allnonemptysubsets :: forall a. Ord a => Set.Set a -> Set.Set (Set.Set a)
+allnonemptysubsets s = Set.delete Set.empty (allsubsets s)
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Explosion and implosion of strings.                                       *)
+(* ------------------------------------------------------------------------- *)
+
+let explode s =
+  let rec exap n l =
+     if n < 0 then l else
+      exap (n - 1) ((String.sub s n 1)::l) in
+  exap (String.length s - 1) [];;
+
+let implode l = itlist (^) l "";;
+
+(* ------------------------------------------------------------------------- *)
+(* Timing; useful for documentation but not logically necessary.             *)
+(* ------------------------------------------------------------------------- *)
+
+let time f x =
+  let start_time = Sys.time() in
+  let result = f x in
+  let finish_time = Sys.time() in
+  print_string
+    ("CPU time (user): "^(string_of_float(finish_time -. start_time)));
+  print_newline();
+  result;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Polymorphic finite partial functions via Patricia trees.                 
+--                                                                          
+-- The point of this strange representation is that it is canonical (equal  
+-- functions have the same encoding) yet reasonably efficient on average.   
+--                                                                          
+-- Idea due to Diego Olivier Fernandez Pons (OCaml list, 2003/11/10).       
+-- -------------------------------------------------------------------------
+{-
+data Func a b
+    = Empty
+    | Leaf Int [(a, b)]
+    | Branch Int Int (Func a b) (Func a b)
+
+-- -------------------------------------------------------------------------
+-- Undefined function.                                                      
+-- -------------------------------------------------------------------------
+
+undefinedFunction = Empty
+
+-- -------------------------------------------------------------------------
+-- In case of equality comparison worries, better use this.                 
+-- -------------------------------------------------------------------------
+
+isUndefined Empty = True
+isUndefined _ = False
+
+-- -------------------------------------------------------------------------
+-- Operation analogous to "map" for functions.                                  
+-- -------------------------------------------------------------------------
+
+mapf f t =
+    case t of
+      Empty -> Empty
+      Leaf h l -> Leaf h (map_list f l)
+      Branch p b l r -> Branch p b (mapf f l) (mapf f r)
+    where
+      map_list f l =
+          case l of
+            [] -> []
+            (x,y) : t -> (x, f y) : map_list f t
+
+-- -------------------------------------------------------------------------
+-- Operations analogous to "fold" for lists.                                
+-- -------------------------------------------------------------------------
+
+foldlFn f a t =
+    case t of
+      Empty -> a
+      Leaf h l -> foldl_list f a l
+      Branch p b l r -> foldlFn f (foldlFn f a l) r
+    where
+      foldl_list f a l =
+          case l of
+            [] -> a
+            (x,y) : t -> foldl_list f (f a x y) t
+
+foldrFn f t a =
+    case t of
+      Empty -> a
+      Leaf h l -> foldr_list f l a
+      Branch p b l r -> foldrFn f l (foldrFn f r a)
+    where
+      foldr_list f l a =
+          case l of
+            [] -> a
+            (x, y) : t -> f x y (foldr_list f t a)
+
+-- -------------------------------------------------------------------------
+-- Mapping to sorted-list representation of the graph, domain and range.    
+-- -------------------------------------------------------------------------
+
+graph f = Set.fromList (foldlFn (\ a x y -> (x,y) : a) [] f)
+
+dom f = Set.fromList (foldlFn (\ a x y -> x :a) [] f)
+
+ran f = Set.fromList (foldlFn (\ a x y -> y : a) [] f)
+-}
+
+-- -------------------------------------------------------------------------
+-- Application.                                                             
+-- -------------------------------------------------------------------------
+
+applyD :: Ord k => Map.Map k a -> k -> a -> Map.Map k a
+applyD m k a = Map.insert k a m
+
+apply :: Ord k => Map.Map k a -> k -> Maybe a
+apply m k = Map.lookup k m
+
+tryApplyD :: Ord k => Map.Map k a -> k -> a -> a
+tryApplyD m k d = fromMaybe d (Map.lookup k m)
+
+tryApplyL :: Ord k => Map.Map k [a] -> k -> [a]
+tryApplyL m k = tryApplyD m k []
+{-
+applyD :: (t -> Maybe b) -> (t -> b) -> t -> b
+applyD f d x = maybe (d x) id (f x)
+
+apply :: (t -> Maybe b) -> t -> b
+apply f = applyD f (\ _ -> error "apply")
+
+tryApplyD :: (t -> Maybe b) -> t -> b -> b
+tryApplyD f a d = maybe d id (f a)
+
+tryApplyL :: (t -> Maybe [a]) -> t -> [a]
+tryApplyL f x = tryApplyD f x []
+-}
+
+defined :: Ord t => Map.Map t a -> t -> Bool
+defined = flip Map.member
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Undefinition.                                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let undefine =
+  let rec undefine_list x l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then t
+          else if c < 0 then l else
+          let t' = undefine_list x t in
+          if t' == t then l else ab::t'
+    | [] -> [] in
+  fun x ->
+    let k = Hashtbl.hash x in
+    let rec und t =
+      match t with
+        Leaf(h,l) when h = k ->
+          let l' = undefine_list x l in
+          if l' == l then t
+          else if l' = [] then Empty
+          else Leaf(h,l')
+      | Branch(p,b,l,r) when k land (b - 1) = p ->
+          if k land b = 0 then
+            let l' = und l in
+            if l' == l then t
+            else (match l' with Empty -> r | _ -> Branch(p,b,l',r))
+          else
+            let r' = und r in
+            if r' == r then t
+            else (match r' with Empty -> l | _ -> Branch(p,b,l,r'))
+      | _ -> t in
+    und;;
+
+(* ------------------------------------------------------------------------- *)
+(* Redefinition and combination.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let (|->),combine =
+  let newbranch p1 t1 p2 t2 =
+    let zp = p1 lxor p2 in
+    let b = zp land (-zp) in
+    let p = p1 land (b - 1) in
+    if p1 land b = 0 then Branch(p,b,t1,t2)
+    else Branch(p,b,t2,t1) in
+  let rec define_list (x,y as xy) l =
+    match l with
+      (a,b as ab)::t ->
+          let c = Pervasives.compare x a in
+          if c = 0 then xy::t
+          else if c < 0 then xy::l
+          else ab::(define_list xy t)
+    | [] -> [xy]
+  and combine_list op z l1 l2 =
+    match (l1,l2) with
+      [],_ -> l2
+    | _,[] -> l1
+    | ((x1,y1 as xy1)::t1,(x2,y2 as xy2)::t2) ->
+          let c = Pervasives.compare x1 x2 in
+          if c < 0 then xy1::(combine_list op z t1 l2)
+          else if c > 0 then xy2::(combine_list op z l1 t2) else
+          let y = op y1 y2 and l = combine_list op z t1 t2 in
+          if z(y) then l else (x1,y)::l in
+  let (|->) x y =
+    let k = Hashtbl.hash x in
+    let rec upd t =
+      match t with
+        Empty -> Leaf (k,[x,y])
+      | Leaf(h,l) ->
+           if h = k then Leaf(h,define_list (x,y) l)
+           else newbranch h t k (Leaf(k,[x,y]))
+      | Branch(p,b,l,r) ->
+          if k land (b - 1) <> p then newbranch p t k (Leaf(k,[x,y]))
+          else if k land b = 0 then Branch(p,b,upd l,r)
+          else Branch(p,b,l,upd r) in
+    upd in
+  let rec combine op z t1 t2 =
+    match (t1,t2) with
+      Empty,_ -> t2
+    | _,Empty -> t1
+    | Leaf(h1,l1),Leaf(h2,l2) ->
+          if h1 = h2 then
+            let l = combine_list op z l1 l2 in
+            if l = [] then Empty else Leaf(h1,l)
+          else newbranch h1 t1 h2 t2
+    | (Leaf(k,lis) as lf),(Branch(p,b,l,r) as br) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z lf l with
+                 Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z lf r with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch k lf p br
+    | (Branch(p,b,l,r) as br),(Leaf(k,lis) as lf) ->
+          if k land (b - 1) = p then
+            if k land b = 0 then
+              (match combine op z l lf with
+                Empty -> r | l' -> Branch(p,b,l',r))
+            else
+              (match combine op z r lf with
+                 Empty -> l | r' -> Branch(p,b,l,r'))
+          else
+            newbranch p br k lf
+    | Branch(p1,b1,l1,r1),Branch(p2,b2,l2,r2) ->
+          if b1 < b2 then
+            if p2 land (b1 - 1) <> p1 then newbranch p1 t1 p2 t2
+            else if p2 land b1 = 0 then
+              (match combine op z l1 t2 with
+                 Empty -> r1 | l -> Branch(p1,b1,l,r1))
+            else
+              (match combine op z r1 t2 with
+                 Empty -> l1 | r -> Branch(p1,b1,l1,r))
+          else if b2 < b1 then
+            if p1 land (b2 - 1) <> p2 then newbranch p1 t1 p2 t2
+            else if p1 land b2 = 0 then
+              (match combine op z t1 l2 with
+                 Empty -> r2 | l -> Branch(p2,b2,l,r2))
+            else
+              (match combine op z t1 r2 with
+                 Empty -> l2 | r -> Branch(p2,b2,l2,r))
+          else if p1 = p2 then
+           (match (combine op z l1 l2,combine op z r1 r2) with
+              (Empty,r) -> r | (l,Empty) -> l | (l,r) -> Branch(p1,b1,l,r))
+          else
+            newbranch p1 t1 p2 t2 in
+  (|->),combine;;
+-}
+
+-- -------------------------------------------------------------------------
+-- Special case of point function.                                          
+-- -------------------------------------------------------------------------
+
+(|=>) :: Ord k => k -> a -> Map.Map k a
+x |=> y = Map.fromList [(x, y)]
+
+-- -------------------------------------------------------------------------
+-- Idiom for a mapping zipping domain and range lists.                      
+-- -------------------------------------------------------------------------
+
+(|->) :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
+(|->) a b m = Map.insert a b m
+
+fpf :: Ord a => Map.Map a b -> a -> Maybe b
+fpf m a = Map.lookup a m
+
+-- -------------------------------------------------------------------------
+-- Grab an arbitrary element.                                               
+-- -------------------------------------------------------------------------
+
+choose :: Map.Map k a -> (k, a)
+choose = Map.findMin
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Install a (trivial) printer for finite partial functions.                 *)
+(* ------------------------------------------------------------------------- *)
+
+let print_fpf (f:('a,'b)func) = print_string "<func>";;
+
+#install_printer print_fpf;;
+
+(* ------------------------------------------------------------------------- *)
+(* Related stuff for standard functions.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let valmod a y f x = if x = a then y else f(x);;
+
+let undef x = failwith "undefined function";;
+
+(* ------------------------------------------------------------------------- *)
+(* Union-find algorithm.                                                     *)
+(* ------------------------------------------------------------------------- *)
+
+type ('a)pnode = Nonterminal of 'a | Terminal of 'a * int;;
+
+type ('a)partition = Partition of ('a,('a)pnode)func;;
+
+let rec terminus (Partition f as ptn) a =
+  match (apply f a) with
+    Nonterminal(b) -> terminus ptn b
+  | Terminal(p,q) -> (p,q);;
+
+let tryterminus ptn a =
+  try terminus ptn a with Failure _ -> (a,1);;
+
+let canonize ptn a = fst(tryterminus ptn a);;
+
+let equivalent eqv a b = canonize eqv a = canonize eqv b;;
+
+let equate (a,b) (Partition f as ptn) =
+  let (a',na) = tryterminus ptn a
+  and (b',nb) = tryterminus ptn b in
+  Partition
+   (if a' = b' then f else
+    if na <= nb then
+       itlist identity [a' |-> Nonterminal b'; b' |-> Terminal(b',na+nb)] f
+    else
+       itlist identity [b' |-> Nonterminal a'; a' |-> Terminal(a',na+nb)] f);;
+
+let unequal = Partition undefined;;
+
+let equated (Partition f) = dom f;;
+
+(* ------------------------------------------------------------------------- *)
+(* First number starting at n for which p succeeds.                          *)
+(* ------------------------------------------------------------------------- *)
+
+let rec first n p = if p(n) then n else first (n +/ Int 1) p;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Meson.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Meson.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Meson.hs
@@ -0,0 +1,547 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Meson where
+
+import Control.Applicative.Error (Failing(..))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (false)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), negative)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.FOL (generalize, list_conj)
+import Data.Logic.Harrison.Lib (setAll, settryfind)
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf)
+import Data.Logic.Harrison.Prolog (renamerule)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (deepen, unify_literals)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set as Set (delete, empty, fold, insert, map, Set, size, union)
+
+-- =========================================================================
+-- Model elimination procedure (MESON version, based on Stickel's PTTP).     
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Example of naivety of tableau prover.                                     
+-- ------------------------------------------------------------------------- 
+
+{-
+START_INTERACTIVE;;
+tab <<forall a. ~(P(a) /\ (forall y z. Q(y) \/ R(z)) /\ ~P(a))>>;;
+
+tab <<forall a. ~(P(a) /\ ~P(a) /\ (forall y z. Q(y) \/ R(z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The interesting example where tableaux connections make the proof longer. 
+-- Unfortuntely this gets hammered by normalization first...                 
+-- ------------------------------------------------------------------------- 
+
+tab <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+      (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+      (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Generation of contrapositives.                                            
+-- ------------------------------------------------------------------------- 
+
+contrapositives :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => Set.Set fof -> Set.Set (Set.Set fof, fof)
+contrapositives cls =
+    if setAll negative cls then Set.insert (Set.map (.~.) cls,false) base else base
+    where base = Set.map (\ c -> (Set.map (.~.) (Set.delete c cls), c)) cls
+
+-- ------------------------------------------------------------------------- 
+-- The core of MESON: ancestor unification or Prolog-style extension.        
+-- ------------------------------------------------------------------------- 
+
+mexpand :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set (Set.Set fof, fof)
+        -> Set.Set fof
+        -> fof
+        -> ((Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int))
+        -> (Map.Map v term, Int, Int) -> Failing (Map.Map v term, Int, Int)
+mexpand rules ancestors g cont (env,n,k) =
+    if n < 0
+    then Failure ["Too deep"]
+    else case settryfind doAncestor ancestors of
+           Success a -> Success a
+           Failure _ -> settryfind doRule rules
+    where
+      doAncestor a =
+          do mp <- unify_literals env g ((.~.) a)
+             cont (mp, n, k)
+      doRule rule =
+          do mp <- unify_literals env g c
+             mexpand' (mp, n - Set.size asm, k')
+          where
+            mexpand' = Set.fold (mexpand rules (Set.insert g ancestors)) cont asm
+            ((asm, c), k') = renamerule k rule
+
+-- ------------------------------------------------------------------------- 
+-- Full MESON procedure.                                                     
+-- ------------------------------------------------------------------------- 
+
+puremeson :: forall fof atom term v f. (FirstOrderFormula fof atom v, Literal fof atom, Term term v f, Atom atom term v, Ord fof) =>
+             Maybe Int -> fof -> Failing ((Map.Map v term, Int, Int), Int)
+puremeson maxdl fm =
+    deepen f 0 maxdl
+    where
+      f n = mexpand rules Set.empty false return (Map.empty, n, 0)
+      rules = Set.fold (Set.union . contrapositives) Set.empty cls
+      cls = simpcnf (specialize (pnf fm))
+
+meson :: forall m fof atom term f v. (FirstOrderFormula fof atom v, PropositionalFormula fof atom, Literal fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+         Maybe Int -> fof -> SkolemT v term m (Set.Set (Failing ((Map.Map v term, Int, Int), Int)))
+meson maxdl fm =
+    askolemize ((.~.)(generalize fm)) >>=
+    return . Set.map (puremeson maxdl . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+{-
+-- ------------------------------------------------------------------------- 
+-- With repetition checking and divide-and-conquer search.                   
+-- ------------------------------------------------------------------------- 
+
+let rec equal env fm1 fm2 =
+  try unify_literals env (fm1,fm2) == env with Failure _ -> false;;
+
+let expand2 expfn goals1 n1 goals2 n2 n3 cont env k =
+   expfn goals1 (fun (e1,r1,k1) ->
+        expfn goals2 (fun (e2,r2,k2) ->
+                        if n2 + r1 <= n3 + r2 then failwith "pair"
+                        else cont(e2,r2,k2))
+              (e1,n2+r1,k1))
+        (env,n1,k);;
+
+let rec mexpand rules ancestors g cont (env,n,k) =
+  if n < 0 then failwith "Too deep"
+  else if exists (equal env g) ancestors then failwith "repetition" else
+  try tryfind (fun a -> cont (unify_literals env (g,negate a),n,k))
+              ancestors
+  with Failure _ -> tryfind
+    (fun r -> let (asm,c),k' = renamerule k r in
+              mexpands rules (g::ancestors) asm cont
+                       (unify_literals env (g,c),n-length asm,k'))
+    rules
+
+and mexpands rules ancestors gs cont (env,n,k) =
+  if n < 0 then failwith "Too deep" else
+  let m = length gs in
+  if m <= 1 then itlist (mexpand rules ancestors) gs cont (env,n,k) else
+  let n1 = n / 2 in
+  let n2 = n - n1 in
+  let goals1,goals2 = chop_list (m / 2) gs in
+  let expfn = expand2 (mexpands rules ancestors) in
+  try expfn goals1 n1 goals2 n2 (-1) cont env k
+  with Failure _ -> expfn goals2 n1 goals1 n2 n1 cont env k;;
+
+let puremeson fm =
+  let cls = simpcnf(specialize(pnf fm)) in
+  let rules = itlist ((@) ** contrapositives) cls [] in
+  deepen (fun n ->
+     mexpand rules [] False (fun x -> x) (undefined,n,0); n) 0;;
+
+let meson fm =
+  let fm1 = askolemize(Not(generalize fm)) in
+  map (puremeson ** list_conj) (simpdnf fm1);;
+
+-- ------------------------------------------------------------------------- 
+-- The Los problem (depth 20) and the Steamroller (depth 53) --- lengthier.  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+{- ***********
+
+let los = meson
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+let steamroller = meson
+ <<((forall x. P1(x) ==> P0(x)) /\ (exists x. P1(x))) /\
+   ((forall x. P2(x) ==> P0(x)) /\ (exists x. P2(x))) /\
+   ((forall x. P3(x) ==> P0(x)) /\ (exists x. P3(x))) /\
+   ((forall x. P4(x) ==> P0(x)) /\ (exists x. P4(x))) /\
+   ((forall x. P5(x) ==> P0(x)) /\ (exists x. P5(x))) /\
+   ((exists x. Q1(x)) /\ (forall x. Q1(x) ==> Q0(x))) /\
+   (forall x. P0(x)
+              ==> (forall y. Q0(y) ==> R(x,y)) \/
+                  ((forall y. P0(y) /\ S0(y,x) /\
+                              (exists z. Q0(z) /\ R(y,z))
+                              ==> R(x,y)))) /\
+   (forall x y. P3(y) /\ (P5(x) \/ P4(x)) ==> S0(x,y)) /\
+   (forall x y. P3(x) /\ P2(y) ==> S0(x,y)) /\
+   (forall x y. P2(x) /\ P1(y) ==> S0(x,y)) /\
+   (forall x y. P1(x) /\ (P2(y) \/ Q1(y)) ==> ~(R(x,y))) /\
+   (forall x y. P3(x) /\ P4(y) ==> R(x,y)) /\
+   (forall x y. P3(x) /\ P5(y) ==> ~(R(x,y))) /\
+   (forall x. (P4(x) \/ P5(x)) ==> exists y. Q0(y) /\ R(x,y))
+   ==> exists x y. P0(x) /\ P0(y) /\
+                   exists z. Q1(z) /\ R(y,z) /\ R(x,y)>>;;
+
+*************** -}
+
+
+-- ------------------------------------------------------------------------- 
+-- Test it.                                                                  
+-- ------------------------------------------------------------------------- 
+
+let prop_1 = time meson
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let prop_2 = time meson
+ <<~ ~p <=> p>>;;
+
+let prop_3 = time meson
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let prop_4 = time meson
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let prop_5 = time meson
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let prop_6 = time meson
+ <<p \/ ~p>>;;
+
+let prop_7 = time meson
+ <<p \/ ~ ~ ~p>>;;
+
+let prop_8 = time meson
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let prop_9 = time meson
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let prop_10 = time meson
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let prop_11 = time meson
+ <<p <=> p>>;;
+
+let prop_12 = time meson
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let prop_13 = time meson
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let prop_14 = time meson
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let prop_15 = time meson
+ <<p ==> q <=> ~p \/ q>>;;
+
+let prop_16 = time meson
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let prop_17 = time meson
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time meson
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time meson
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time meson
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time meson
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time meson
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time meson
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time meson
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time meson
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time meson
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time meson
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time meson
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time meson
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time meson
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time meson
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time meson
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time meson
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time meson
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time meson
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time meson
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time meson
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time meson
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+let p39 = time meson
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time meson
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time meson
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time meson
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+let p43 = time meson
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+let p44 = time meson
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+let p45 = time meson
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+let p46 = time meson
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time meson
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time meson
+ <<P(f((a),b),f(b,c)) /\
+  P(f(b,c),f(a,c)) /\
+  (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+  ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time meson
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time meson
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time meson
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- ** Amazingly, this still seems non-trivial... in HOL it works at depth 45!
+
+let gilmore_1 = time meson
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+ ** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time meson
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time meson
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time meson
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time meson
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time meson
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time meson
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This is still a very hard problem
+
+let gilmore_9 = time meson
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Translation of Gilmore procedure using separate definitions.              
+-- ------------------------------------------------------------------------- 
+
+let gilmore_9a = time meson
+ <<(forall x y. P(x,y) <=>
+                forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+   ==> forall x. exists y. forall z.
+             (P(y,x) ==> (P(x,z) ==> P(x,y))) /\
+             (P(x,y) ==> (~P(x,z) ==> P(y,x) /\ P(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time meson
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- The "connections make things worse" example once again.                   
+-- ------------------------------------------------------------------------- 
+
+meson <<~p /\ (p \/ q) /\ (r \/ s) /\ (~q \/ t \/ u) /\
+        (~r \/ ~t) /\ (~r \/ ~u) /\ (~q \/ v \/ w) /\
+        (~s \/ ~v) /\ (~s \/ ~w) ==> false>>;;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Normal.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Normal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Normal.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+-- | Versions of the normal form functions in Prop for FirstOrderFormula.
+module Data.Logic.Harrison.Normal
+    ( trivial
+    , simpdnf
+    , simpdnf'
+    , simpcnf
+    , simpcnf'
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fromFirstOrder)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable, negated)
+import Data.Logic.Failing (failing)
+import Data.Logic.Harrison.Lib (allpairs, setAny)
+import Data.Logic.Harrison.Skolem (nnf)
+import qualified Data.Set.Extra as Set (distrib, empty, filter, intersection, isProperSubsetOf, map, null, or, partition, Set, singleton, union)
+import Prelude hiding (negate)
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.  (dsf: now set)
+-- ------------------------------------------------------------------------- 
+
+distrib' :: (Eq formula, Ord formula) => Set.Set (Set.Set formula) -> Set.Set (Set.Set formula) -> Set.Set (Set.Set formula)
+distrib' s1 s2 = allpairs (Set.union) s1 s2
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Negatable lit, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (neg, pos) = Set.partition negated lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: (FirstOrderFormula fof atom v, Eq fof, Ord fof) =>
+           fof -> Set.Set (Set.Set fof)
+simpdnf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at _ = Set.singleton (Set.singleton fm)
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf (nnf fm))
+
+purednf :: (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purednf fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = Set.singleton (Set.singleton fm)
+      co (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      co (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      co _ = Set.singleton (Set.singleton fm)
+      tf = Set.singleton . Set.singleton . fromBool
+      at _ = Set.singleton (Set.singleton fm)
+
+simpdnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Formula lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+simpdnf' fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.empty
+      tf True = Set.singleton Set.empty
+      at = Set.singleton . Set.singleton . atomic
+      def = Set.filter keep djs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) djs)
+      djs = Set.filter (not . trivial) (purednf' (nnf fm))
+
+purednf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) =>
+            fof -> Set.Set (Set.Set lit)
+purednf' fm =
+    foldFirstOrder (\ _ _ _ -> x) co (\ _ -> x) (\ _ -> x)  fm
+    where
+      -- co :: Combination formula -> Set.Set (Set.Set lit)
+      co (BinOp p (:&:) q) = Set.distrib (purednf' p) (purednf' q)
+      co (BinOp p (:|:) q) = Set.union (purednf' p) (purednf' q)
+      co _ = x
+      -- x :: Set.Set (Set.Set lit)
+      x = failing (const (error "purednf'")) (Set.singleton . Set.singleton) (fromFirstOrder id fm)
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+-- It would be nice to share code this way, but the caller needs to
+-- specify the intermediate lit type, which is a pain.
+-- simpcnf :: forall fof lit atom v. (FirstOrderFormula fof atom v, Ord fof, Literal lit atom v, Eq lit, Ord lit) => fof -> Set.Set (Set.Set fof)
+-- simpcnf fm = Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+
+simpcnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+simpcnf fm =
+    -- Set.map (Set.map (fromLiteral id :: lit -> fof)) . simpcnf' $ fm
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ _ _ = def
+      co _ = def
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      at x = Set.singleton (Set.singleton (atomic x))
+      -- Discard any clause that is the proper subset of another clause
+      def = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+
+purecnf :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Set.Set (Set.Set fof)
+purecnf fm = Set.map (Set.map ({-simplify .-} (.~.))) (purednf (nnf ((.~.) fm)))
+
+-- Alternative versions, these should be merged
+
+simpcnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+simpcnf' fm =
+    foldFirstOrder (\ _ _ _ -> cjs') co tf at fm
+    where
+      co _ = cjs'
+      at = Set.singleton . Set.singleton . atomic -- foldAtomEq (\ _ _ -> cjs') tf (\ _ _ -> cjs')
+      tf False = Set.singleton Set.empty
+      tf True = Set.empty
+      -- Discard any clause that is the proper subset of another clause
+      cjs' = Set.filter keep cjs
+      keep x = not (Set.or (Set.map (`Set.isProperSubsetOf` x) cjs))
+      cjs = Set.filter (not . trivial) (purecnf' (nnf fm)) -- :: Set.Set (Set.Set lit)
+
+-- | CNF: (a | b | c) & (d | e | f)
+purecnf' :: forall lit fof atom v. (FirstOrderFormula fof atom v, Literal lit atom, Ord lit) => fof -> Set.Set (Set.Set lit)
+purecnf' fm = Set.map (Set.map (.~.)) (purednf' (nnf ((.~.) fm)))
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prolog.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prolog.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prolog.hs
@@ -0,0 +1,194 @@
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Prolog where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Term (Term(vt))
+import Data.String (IsString (fromString))
+import Data.Logic.Harrison.FOL (fv, subst, list_conj)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- ========================================================================= 
+-- Backchaining procedure for Horn clauses, and toy Prolog implementation.   
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Rename a rule.                                                            
+-- ------------------------------------------------------------------------- 
+
+renamerule :: forall fof atom term v f. (FirstOrderFormula fof atom v, {-Formula fof term v,-} Atom atom term v, Term term v f, Ord fof) =>
+              Int -> (Set.Set fof, fof) -> ((Set.Set fof, fof), Int)
+renamerule k (asm,c) =
+    ((Set.map inst asm, inst c), k + Set.size fvs)
+    where
+      fvs = fv (list_conj (Set.insert c asm)) :: Set.Set v
+      vvs = Map.fromList (map (\ (v, i) -> (v, vt (fromString ("_" ++ show i)))) (zip (Set.toList fvs) [k..])) :: Map.Map v term
+      inst = subst vvs :: fof -> fof
+
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Basic prover for Horn clauses based on backchaining with unification.     *)
+(* ------------------------------------------------------------------------- *)
+
+let rec backchain rules n k env goals =
+  match goals with
+    [] -> env
+  | g::gs ->
+     if n = 0 then failwith "Too deep" else
+     tryfind (fun rule ->
+        let (a,c),k' = renamerule k rule in
+        backchain rules (n - 1) k' (unify_literals env (c,g)) (a @ gs))
+     rules;;
+
+let hornify cls =
+  let pos,neg = partition positive cls in
+  if length pos > 1 then failwith "non-Horn clause"
+  else (map negate neg,if pos = [] then False else hd pos);;
+
+let hornprove fm =
+  let rules = map hornify (simpcnf(skolemize(Not(generalize fm)))) in
+  deepen (fun n -> backchain rules n 0 undefined [False],n) 0;;
+
+(* ------------------------------------------------------------------------- *)
+(* A Horn example.                                                           *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let p32 = hornprove
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x))
+   ==> (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* A non-Horn example.                                                       *)
+(* ------------------------------------------------------------------------- *)
+
+(****************
+
+hornprove <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+**********)
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* Parsing rules in a Prolog-like syntax.                                    *)
+(* ------------------------------------------------------------------------- *)
+
+let parserule s =
+  let c,rest =
+    parse_formula (parse_infix_atom,parse_atom) [] (lex(explode s)) in
+  let asm,rest1 =
+    if rest <> [] & hd rest = ":-"
+    then parse_list ","
+          (parse_formula (parse_infix_atom,parse_atom) []) (tl rest)
+    else [],rest in
+  if rest1 = [] then (asm,c) else failwith "Extra material after rule";;
+
+(* ------------------------------------------------------------------------- *)
+(* Prolog interpreter: just use depth-first search not iterative deepening.  *)
+(* ------------------------------------------------------------------------- *)
+
+let simpleprolog rules gl =
+  backchain (map parserule rules) (-1) 0 undefined [parse gl];;
+
+(* ------------------------------------------------------------------------- *)
+(* Ordering example.                                                         *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+let lerules = ["0 <= X"; "S(X) <= S(Y) :- X <= Y"];;
+
+simpleprolog lerules "S(S(0)) <= S(S(S(0)))";;
+
+(*** simpleprolog lerules "S(S(0)) <= S(0)";;
+ ***)
+
+let env = simpleprolog lerules "S(S(0)) <= X";;
+apply env "X";;
+END_INTERACTIVE;;
+
+(* ------------------------------------------------------------------------- *)
+(* With instantiation collection to produce a more readable result.          *)
+(* ------------------------------------------------------------------------- *)
+
+let prolog rules gl =
+  let i = solve(simpleprolog rules gl) in
+  mapfilter (fun x -> Atom(R("=",[Var x; apply i x]))) (fv(parse gl));;
+
+(* ------------------------------------------------------------------------- *)
+(* Example again.                                                            *)
+(* ------------------------------------------------------------------------- *)
+
+START_INTERACTIVE;;
+prolog lerules "S(S(0)) <= X";;
+
+(* ------------------------------------------------------------------------- *)
+(* Append example, showing symmetry between inputs and outputs.              *)
+(* ------------------------------------------------------------------------- *)
+
+let appendrules =
+  ["append(nil,L,L)"; "append(H::T,L,H::A) :- append(T,L,A)"];;
+
+prolog appendrules "append(1::2::nil,3::4::nil,Z)";;
+
+prolog appendrules "append(1::2::nil,Y,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,3::4::nil,1::2::3::4::nil)";;
+
+prolog appendrules "append(X,Y,1::2::3::4::nil)";;
+
+(* ------------------------------------------------------------------------- *)
+(* However this way round doesn't work.                                      *)
+(* ------------------------------------------------------------------------- *)
+
+(***
+ *** prolog appendrules "append(X,3::4::nil,X)";;
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* A sorting example (from Lloyd's "Foundations of Logic Programming").      *)
+(* ------------------------------------------------------------------------- *)
+
+let sortrules =
+ ["sort(X,Y) :- perm(X,Y),sorted(Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+prolog sortrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+(* ------------------------------------------------------------------------- *)
+(* Yet with a simple swap of the first two predicates...                     *)
+(* ------------------------------------------------------------------------- *)
+
+let badrules =
+ ["sort(X,Y) :- sorted(Y), perm(X,Y)";
+  "sorted(nil)";
+  "sorted(X::nil)";
+  "sorted(X::Y::Z) :- X <= Y, sorted(Y::Z)";
+  "perm(nil,nil)";
+  "perm(X::Y,U::V) :- delete(U,X::Y,Z), perm(Z,V)";
+  "delete(X,X::Y,Y)";
+  "delete(X,Y::Z,Y::W) :- delete(X,Z,W)";
+  "0 <= X";
+  "S(X) <= S(Y) :- X <= Y"];;
+
+(*** This no longer works
+
+prolog badrules
+  "sort(S(S(S(S(0))))::S(0)::0::S(S(0))::S(0)::nil,X)";;
+
+ ***)
+END_INTERACTIVE;;                           
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prop.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Prop.hs
@@ -0,0 +1,450 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Harrison.Prop
+    ( eval
+    , atoms
+    , onAllValuations
+    , TruthTable
+    , TruthTableRow
+    , truthTable
+    , tautology
+    , unsatisfiable
+    , satisfiable
+    , rawdnf
+    , purednf
+    , dnf
+    , dnf'
+    , trivial
+    , psimplify
+    , nnf
+    , simpdnf
+    , simpcnf
+    , positive
+    , negative
+    , negate
+    , distrib
+    , list_disj
+    , list_conj
+    -- previously unexported
+    , pSubst
+    , dual
+    , nenf
+    , mkLits
+    , allSatValuations
+    , dnf0
+    , cnf
+    , cnf'
+    ) where
+
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, ifElse, true)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal(foldLiteral), toPropositional)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Formulas.Propositional (atom_union, on_atoms)
+import Data.Logic.Harrison.Lib (distrib', fpf, setAny)
+import qualified Data.Map as Map (empty, findWithDefault, insert, Map)
+import qualified Data.Set as Set (empty, filter, fold, fromList, intersection, isProperSubsetOf, map, minView, null, partition, Set, singleton, toAscList, union)
+import Prelude hiding (negate)
+
+-- type Map a = Map.Map a Bool
+-- m0 = Map.empty
+-- ins :: forall a. Ord a => a -> Bool -> Map a -> Map a
+-- ins = Map.insert
+-- m ! k = Map.findWithDefault False k m
+
+-- ------------------------------------------------------------------------- 
+-- Parsing of propositional formulas.                                        
+-- ------------------------------------------------------------------------- 
+
+{-
+let parse_propvar vs inp =
+  match inp with
+    p::oinp when p /= "(" -> Atom(P(p)),oinp
+  | _ -> failwith "parse_propvar";;
+
+let parse_prop_formula = make_parser
+  (parse_formula ((fun _ _ -> failwith ""),parse_propvar) []);;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Set this up as default for quotations.                                    
+-- ------------------------------------------------------------------------- 
+
+{-
+let default_parser = parse_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Printer.                                                                  
+-- ------------------------------------------------------------------------- 
+
+{-
+let print_propvar prec p = print_string(pname p);;
+
+let print_prop_formula = print_qformula print_propvar;;
+
+#install_printer print_prop_formula;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Interpretation of formulas.                                               
+-- ------------------------------------------------------------------------- 
+
+eval :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Map.Map atomic Bool -> Bool
+eval fm v =
+    foldPropositional co id at fm
+    where
+      co ((:~:) p) = not (eval p v)
+      co (BinOp p (:&:) q) = eval p v && eval q v
+      co (BinOp p (:|:) q) = eval p v || eval q v
+      co (BinOp p (:=>:) q) = not (eval p v) || eval q v
+      co (BinOp p (:<=>:) q) = eval p v == eval q v
+      at x = Map.findWithDefault False x v
+
+{-
+START_INTERACTIVE;;
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> false | P"r" -> true);;
+
+eval <<p /\ q ==> q /\ r>>
+     (function P"p" -> true | P"q" -> true | P"r" -> false);;
+END_INTERACTIVE;;
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Return the set of propositional variables in a formula.                   
+-- ------------------------------------------------------------------------- 
+
+atoms :: Ord atomic => PropositionalFormula formula atomic => formula -> Set.Set atomic
+atoms = atom_union Set.singleton
+
+-- ------------------------------------------------------------------------- 
+-- Code to print out truth tables.                                           
+-- ------------------------------------------------------------------------- 
+
+onAllValuations :: (Ord a) =>
+                   (r -> r -> r)         -- ^ Combine function for result type
+                -> (Map.Map a Bool -> r) -- ^ The substitution function
+                -> Map.Map a Bool        -- ^ The default valuation function for atoms not in ps
+                -> Set.Set a             -- ^ The variables to vary
+                -> r
+onAllValuations _ subfn v ps | Set.null ps = subfn v
+onAllValuations append subfn v ps =
+    case Set.minView ps of
+      Nothing -> error "onAllValuations"
+      Just (p, ps') ->
+          append -- Do the valuations of the remaining variables with  set to false
+                 (onAllValuations append subfn (Map.insert p False v) ps')
+                 -- Do the valuations of the remaining variables with  set to true
+                 (onAllValuations append subfn (Map.insert p True v) ps')
+
+type TruthTableRow = ([Bool], Bool)
+type TruthTable a = ([a], [TruthTableRow])
+
+truthTable :: forall formula atom. (PropositionalFormula formula atom, Eq atom, Ord atom) =>
+              formula -> TruthTable atom
+truthTable fm =
+    (atl, onAllValuations (++) mkRow Map.empty ats)
+    where
+      mkRow :: Map.Map atom Bool      -- ^ The current variable assignment
+            -> [TruthTableRow]          -- ^ The variable assignments and the formula value
+      mkRow v = [(map (\ k -> Map.findWithDefault False k v) atl, eval fm v)]
+      atl = Set.toAscList ats
+      ats = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- Recognizing tautologies.                                                  
+-- ------------------------------------------------------------------------- 
+
+tautology :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+tautology fm = onAllValuations (&&) (eval fm) Map.empty (atoms fm)
+
+-- ------------------------------------------------------------------------- 
+-- Related concepts.                                                         
+-- ------------------------------------------------------------------------- 
+
+
+unsatisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+unsatisfiable fm = tautology ((.~.) fm)
+
+satisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
+satisfiable = not . unsatisfiable
+
+-- ------------------------------------------------------------------------- 
+-- Substitution operation.                                                   
+-- ------------------------------------------------------------------------- 
+
+-- pSubst :: Ord a => Map.Map a (Formula a) -> Formula a -> Formula a
+pSubst :: (PropositionalFormula formula atomic, Ord atomic) => Map.Map atomic formula -> formula -> formula
+pSubst subfn fm = on_atoms (\ p -> maybe (atomic p) id (fpf subfn p)) fm
+
+-- ------------------------------------------------------------------------- 
+-- Dualization.                                                              
+-- ------------------------------------------------------------------------- 
+
+dual :: forall formula atomic. (PropositionalFormula formula atomic) => formula -> formula
+dual fm =
+    foldPropositional co (fromBool . not) at fm
+    where
+      co ((:~:) _) = fm
+      co (BinOp p (:&:) q) = dual p .|. dual q
+      co (BinOp p (:|:) q) = dual p .&. dual q
+      co _ = error "dual: Formula involves connectives ==> or <=>";;
+      at = atomic
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+psimplify1 :: (PropositionalFormula r a, Eq r) => r -> r
+psimplify1 fm =
+    foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      simplifyCombine ((:~:) fm') = foldPropositional simplifyNotCombine (fromBool . not) (\ _ -> fm) fm'
+      simplifyCombine (BinOp l op r) =
+          case (asBool l, op, asBool r) of
+            (Just True,  (:&:), _         ) -> r
+            (Just False, (:&:), _         ) -> false
+            (_,          (:&:), Just True ) -> l
+            (_,          (:&:), Just False) -> false
+            (Just True,  (:|:), _         ) -> true
+            (Just False, (:|:), _         ) -> r
+            (_,          (:|:), Just True ) -> true
+            (_,          (:|:), Just False) -> l
+            (Just True,  (:=>:), _         ) -> r
+            (Just False, (:=>:), _         ) -> true
+            (_,          (:=>:), Just True ) -> true
+            (_,          (:=>:), Just False) -> (.~.) l
+            (Just True,  (:<=>:), _         ) -> r
+            (Just False, (:<=>:), _         ) -> (.~.) r
+            (_,          (:<=>:), Just True ) -> l
+            (_,          (:<=>:), Just False) -> (.~.) l
+            _ -> fm
+
+      simplifyNotCombine ((:~:) p) = p
+      simplifyNotCombine _ = fm
+
+psimplify :: forall formula atomic. (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+psimplify fm =
+    foldPropositional c (\ _ -> fm) (\ _ -> fm) fm
+    where
+      c :: Combination formula -> formula
+      c ((:~:) p) = psimplify1 ((.~.) (psimplify p))
+      c (BinOp p op q) = psimplify1 (binop (psimplify p) op (psimplify q))
+
+-- ------------------------------------------------------------------------- 
+-- Some operations on literals.                                              
+-- ------------------------------------------------------------------------- 
+
+negative :: forall lit atom. Literal lit atom => lit -> Bool
+negative lit =
+    foldLiteral neg tf a lit
+    where
+      neg _ = True
+      tf = not
+      a _ = False
+
+positive :: Literal lit atom => lit -> Bool
+positive = not . negative
+
+negate :: PropositionalFormula formula atomic => formula -> formula
+negate lit =
+    foldPropositional c (fromBool . not) a lit
+    where
+      c ((:~:) p) = p
+      c _ = (.~.) lit
+      a _ = (.~.) lit
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf' :: PropositionalFormula formula atomic => formula -> formula
+nnf' fm =
+    foldPropositional nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfCombine ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf' ((.~.) p) .|. (nnf' q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf' p .&. nnf' q) .|. (nnf' ((.~.) p) .&. nnf' ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf' p .&. nnf' q
+      nnfCombine (BinOp p (:|:) q) = nnf' p .|. nnf' q
+      nnfNotCombine ((:~:) p) = nnf' p
+      nnfNotCombine (BinOp p (:&:) q) = nnf' ((.~.) p) .|. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf' ((.~.) p) .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf' p .&. nnf' ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' ((.~.) q)) .|. nnf' ((.~.) p) .&. nnf' q
+
+-- ------------------------------------------------------------------------- 
+-- Roll in simplification.                                                   
+-- ------------------------------------------------------------------------- 
+
+nnf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nnf = nnf' . psimplify
+
+-- ------------------------------------------------------------------------- 
+-- Simple negation-pushing when we don't care to distinguish occurrences.    
+-- ------------------------------------------------------------------------- 
+
+nenf' :: PropositionalFormula formula atomic => formula -> formula
+nenf' fm =
+    foldPropositional nenfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nenfCombine ((:~:) p) = foldPropositional nenfNotCombine (\ _ -> fm) (\ _ -> fm) p
+      nenfCombine (BinOp p (:&:) q) = nenf' p .&. nenf' q
+      nenfCombine (BinOp p (:|:) q) = nenf' p .|. nenf' q
+      nenfCombine (BinOp p (:=>:) q) = nenf' ((.~.) p) .|. nenf' q
+      nenfCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' q
+      nenfNotCombine ((:~:) p) = p
+      nenfNotCombine (BinOp p (:&:) q) = nenf' ((.~.) p) .|. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:|:) q) = nenf' ((.~.) p) .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:=>:) q) = nenf' p .&. nenf' ((.~.) q)
+      nenfNotCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' ((.~.) q) -- really?  how is this asymmetrical?
+
+nenf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
+nenf = nenf' . psimplify
+
+{-
+# Not (prime 2) ->
+  <<~(~(((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1))>>
+
+# nenf (Not (prime 2)) -> 
+  <<((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1>>
+
+> pretty ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N)))
+     (out0 ⇔ x0 ∧ y0) ∧ ¬out1 ∧ out1 ∧ ¬out0
+
+> pretty (nenf ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N))))
+     (out0 ⇔ x0 ∨ y0) ∨ ¬out1 ∨ out1 ∨ ¬out0
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Disjunctive normal form (DNF) via truth tables.                           
+-- ------------------------------------------------------------------------- 
+
+list_conj :: (PropositionalFormula formula atomic, Ord formula) => Set.Set formula -> formula
+list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
+
+list_disj :: PropositionalFormula formula atomic => Set.Set formula -> formula
+list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
+
+mkLits :: (PropositionalFormula formula atomic, Ord formula, Ord atomic) =>
+          Set.Set formula -> Map.Map atomic Bool -> formula
+mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
+
+allSatValuations :: Ord a => (Map.Map a Bool -> Bool) -> Map.Map a Bool -> Set.Set a -> [Map.Map a Bool]
+allSatValuations subfn v pvs =
+    case Set.minView pvs of
+      Nothing -> if subfn v then [v] else []
+      Just (p, ps) -> (allSatValuations subfn (Map.insert p False v) ps) ++
+                      (allSatValuations subfn (Map.insert p True v) ps)
+
+dnf0 :: forall formula atomic. (PropositionalFormula formula atomic, Ord atomic, Ord formula) => formula -> formula
+dnf0 fm =
+    list_disj (Set.fromList (map (mkLits (Set.map atomic pvs)) satvals))
+    where
+      satvals = allSatValuations (eval fm) Map.empty pvs
+      pvs = atoms fm
+
+-- ------------------------------------------------------------------------- 
+-- DNF via distribution.                                                     
+-- ------------------------------------------------------------------------- 
+
+distrib :: PropositionalFormula formula atomic => formula -> formula
+distrib fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) s) =
+          foldPropositional c' tf a s
+          where c' (BinOp q (:|:) r) = distrib (p .&. q) .|. distrib (p .&. r)
+                c' _ =
+                    foldPropositional c'' tf a p
+                    where c'' (BinOp q (:|:) r) = distrib (q .&. s) .|. distrib (r .&. s)
+                          c'' _ = fm
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+rawdnf :: PropositionalFormula formula atomic => formula -> formula
+rawdnf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib (rawdnf p .&. rawdnf q)
+      c (BinOp p (:|:) q) = rawdnf p .|. rawdnf q
+      c _ = fm
+      tf _ = fm
+      a _ = fm
+
+-- ------------------------------------------------------------------------- 
+-- A version using a list representation.                                    
+-- ------------------------------------------------------------------------- 
+
+purednf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purednf fm =
+    foldPropositional c tf a fm
+    where
+      c (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
+      c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
+      c ((:~:) p) = Set.map (Set.map (.~.)) (purednf p)
+      c _ = error "purednf" -- Set.singleton (Set.singleton fm)
+      tf x = Set.singleton (Set.singleton (fromBool x))
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Filtering out trivial disjuncts (in this guise, contradictory).           
+-- ------------------------------------------------------------------------- 
+
+trivial :: (Literal lit atom, Ord lit) => Set.Set lit -> Bool
+trivial lits =
+    not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
+    where (pos, neg) = Set.partition positive lits
+
+-- ------------------------------------------------------------------------- 
+-- With subsumption checking, done very naively (quadratic).                 
+-- ------------------------------------------------------------------------- 
+
+simpdnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpdnf fm =
+    foldPropositional c tf a fm
+    where
+      c :: Combination pf -> Set.Set (Set.Set lit)
+      c _ = Set.filter (\ d -> not (setAny (\ d' -> Set.isProperSubsetOf d' d) djs)) djs
+          where djs = Set.filter (not . trivial) (purednf (nnf fm))
+      tf = ifElse (Set.singleton Set.empty) Set.empty
+      a :: atom -> Set.Set (Set.Set lit)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+-- ------------------------------------------------------------------------- 
+-- Mapping back to a formula.                                                
+-- ------------------------------------------------------------------------- 
+
+dnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+dnf = list_disj . Set.map (list_conj . Set.map (toPropositional id))
+
+dnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+dnf' = dnf . (simpdnf :: pf -> Set.Set (Set.Set pf))
+
+-- ------------------------------------------------------------------------- 
+-- Conjunctive normal form (CNF) by essentially the same code.               
+-- ------------------------------------------------------------------------- 
+
+purecnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
+
+simpcnf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
+simpcnf fm =
+    foldPropositional c tf a fm
+    where
+      tf = ifElse Set.empty (Set.singleton Set.empty)
+      -- Discard any clause that is the proper subset of another clause
+      c _ = Set.filter keep cjs
+      keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
+      cjs = Set.filter (not . trivial) (purecnf fm)
+      a x = Set.singleton (Set.singleton (atomic x))
+
+cnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
+cnf = list_conj . Set.map (list_disj . Set.map (toPropositional id))
+
+cnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
+cnf' = cnf . (simpcnf :: pf -> Set.Set (Set.Set pf))
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/PropExamples.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/PropExamples.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/PropExamples.hs
@@ -0,0 +1,392 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+module Data.Logic.Harrison.PropExamples
+    ( Atom(..)
+    , N
+    , prime
+    , ramsey
+    , tests
+    ) where
+
+import Data.Bits (Bits, shiftR)
+import Data.Logic.Classes.Combine ((.<=>.), (.=>.), (.&.), (.|.), Combinable, Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (true, false)
+import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), botFixity)
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Harrison.Lib (allsets)
+import Data.Logic.Harrison.Prop (tautology, list_conj, list_disj, psimplify)
+import Data.Logic.Types.Propositional (Formula(..))
+import qualified Data.Set as Set
+import Prelude hiding (sum)
+import Test.HUnit
+import Text.PrettyPrint (text)
+
+tests :: Test
+tests = TestList [test01, test02, test03]
+
+-- ========================================================================= 
+-- Some propositional formulas to test, and functions to generate classes.   
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Generate assertion equivalent to R(s,t) <= n for the Ramsey number R(s,t) 
+-- ------------------------------------------------------------------------- 
+
+data Atom a = P String a (Maybe a) deriving (Eq, Ord, Show)
+
+instance Pretty (Atom N) where
+    pretty (P s n mm) = text (s ++ show n ++ maybe "" (\ m -> "." ++ show m) mm)
+
+instance HasFixity (Atom N) where
+    fixity = const botFixity
+
+type N = Int
+
+ramsey :: forall formula.
+          (PropositionalFormula formula (Atom N), Ord formula) =>
+          Int -> Int -> N -> formula
+ramsey s t n =
+  let vertices = Set.fromList [1 .. n] in
+  let yesgrps = Set.map (allsets (2 :: Int)) (allsets s vertices)
+      nogrps = Set.map (allsets (2 :: Int)) (allsets t vertices) in
+  let e xs = let [m, n] = Set.toAscList xs in C.atomic (P "p" m (Just n)) in
+  list_disj (Set.map (list_conj . Set.map e) yesgrps) .|. list_disj (Set.map (list_conj . Set.map (\ p -> (.~.)(e p))) nogrps)
+
+-- ------------------------------------------------------------------------- 
+-- Some currently tractable examples.                                        
+-- ------------------------------------------------------------------------- 
+
+test01 :: Test
+test01 = TestList [{- TestCase (assertEqual "ramsey 3 3 4"
+                                             (Combine
+                                              (BinOp
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp
+                                                   (Atom (P "p" 1 (Just 4)))
+                                                   (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 2 (Just 4)))
+                                                     (:&:)
+                                                     (Atom (P "p" 1 (Just 2)))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp
+                                                     (Atom (P "p" 1 (Just 4)))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 3 (Just 4)))
+                                                       (:&:)
+                                                       (Atom (P "p" 1 (Just 3)))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 2 (Just 4)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 3 (Just 4)))
+                                                         (:&:)
+                                                         (Atom (P "p" 2 (Just 3)))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Atom (P "p" 1 (Just 3)))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Atom (P "p" 2 (Just 3)))
+                                                         (:&:)
+                                                         (Atom (P "p" 1 (Just 2)))))))))))))
+                                               (:|:)
+                                               (Combine
+                                                (BinOp
+                                                 (Combine
+                                                  (BinOp (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 4))))) (:&:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      ((:~:) (Atom (P "p" 1 (Just 2)))))))))
+                                                 (:|:)
+                                                 (Combine
+                                                  (BinOp
+                                                   (Combine
+                                                    (BinOp (Combine
+                                                            ((:~:) (Atom (P "p" 1 (Just 4)))))
+                                                     (:&:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))))))
+                                                   (:|:)
+                                                   (Combine
+                                                    (BinOp
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 2 (Just 4)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 3 (Just 4)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))))))
+                                                     (:|:)
+                                                     (Combine
+                                                      (BinOp
+                                                       (Combine
+                                                        ((:~:) (Atom (P "p" 1 (Just 3)))))
+                                                       (:&:)
+                                                       (Combine
+                                                        (BinOp
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 2 (Just 3)))))
+                                                         (:&:)
+                                                         (Combine
+                                                          ((:~:) (Atom (P "p" 1 (Just 2)))))))))))))))))
+                                         (ramsey 3 3 4 :: Formula (Atom N))), -}
+                   TestCase (assertEqual "tautology (ramsey 3 3 5)" False (tautology (ramsey 3 3 5 :: Formula (Atom N)))),
+                   TestCase (assertEqual "tautology (ramsey 3 3 6)" True (tautology (ramsey 3 3 6 :: Formula (Atom N))))]
+
+-- ------------------------------------------------------------------------- 
+-- Half adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+halfsum :: forall formula. Combinable formula => formula -> formula -> formula
+halfsum x y = x .<=>. ((.~.) y)
+
+halfcarry :: forall formula. Combinable formula => formula -> formula -> formula
+halfcarry x y = x .&. y
+
+ha :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula
+ha x y s c = (s .<=>. halfsum x y) .&. (c .<=>. halfcarry x y)
+
+-- ------------------------------------------------------------------------- 
+-- Full adder.                                                               
+-- ------------------------------------------------------------------------- 
+
+carry :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+carry x y z = (x .&. y) .|. ((x .|. y) .&. z)
+
+sum :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+sum x y z = halfsum (halfsum x y) z
+
+fa :: forall formula. Combinable formula => formula -> formula -> formula -> formula -> formula -> formula
+fa x y z s c = (s .<=>. sum x y z) .&. (c .<=>. carry x y z)
+
+-- ------------------------------------------------------------------------- 
+-- Useful idiom.                                                             
+-- ------------------------------------------------------------------------- 
+
+conjoin :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a) => (a -> formula) -> Set.Set a -> formula
+conjoin f l = list_conj (Set.map f l)
+
+-- ------------------------------------------------------------------------- 
+-- n-bit ripple carry adder with carry c(0) propagated in and c(n) out.      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> formula
+ripplecarry x y c out n =
+    conjoin (\ i -> fa (x i) (y i) (c i) (out i) (c(i + 1))) (Set.fromList [0 .. (n - 1)])
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+mk_index :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> formula
+mk_index x i = C.atomic (P x i Nothing)
+mk_index2 :: forall formula a. PropositionalFormula formula (Atom a) => String -> a -> a -> formula
+mk_index2 x i j = C.atomic (P x i (Just j))
+
+test02 = TestCase (assertEqual "ripplecarry x y c out 2"
+                               (Combine (BinOp (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 1 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 1 Nothing)))))))) (:&:)
+                                                         (Combine (BinOp (Atom (P "C" 2 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:&:) (Atom (P "Y" 1 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 1 Nothing)) (:|:) (Atom (P "Y" 1 Nothing)))) (:&:) (Atom (P "C" 1 Nothing)))))))))) (:&:)
+                                         (Combine (BinOp (Combine (BinOp (Atom (P "OUT" 0 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:<=>:) (Combine ((:~:) (Atom (P "Y" 0 Nothing)))))) (:<=>:) (Combine ((:~:) (Atom (P "C" 0 Nothing)))))))) (:&:)
+                                                   (Combine (BinOp (Atom (P "C" 1 Nothing)) (:<=>:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:&:) (Atom (P "Y" 0 Nothing)))) (:|:) (Combine (BinOp (Combine (BinOp (Atom (P "X" 0 Nothing)) (:|:) (Atom (P "Y" 0 Nothing)))) (:&:) (Atom (P "C" 0 Nothing))))))))))))
+                               {- <<((OUT_0 <=> (X_0 <=> ~Y_0) <=> ~C_0) /\
+                                      (C_1 <=> X_0 /\ Y_0 \/ (X_0 \/ Y_0) /\ C_0)) /\
+                                     (OUT_1 <=> (X_1 <=> ~Y_1) <=> ~C_1) /\
+                                     (C_2 <=> X_1 /\ Y_1 \/ (X_1 \/ Y_1) /\ C_1)>> -}
+                               (let [x, y, out, c] = map mk_index ["X", "Y", "OUT", "C"] in
+                                ripplecarry x y c out 2 :: Formula (Atom N)))
+
+-- ------------------------------------------------------------------------- 
+-- Special case with 0 instead of c(0).                                      
+-- ------------------------------------------------------------------------- 
+
+ripplecarry0 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry0 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then false else c i) out n)
+
+-- ------------------------------------------------------------------------- 
+-- Carry-select adder                                                        
+-- ------------------------------------------------------------------------- 
+
+ripplecarry1 :: forall formula atomic a. (PropositionalFormula formula atomic, Ord formula, Ord a, Num a, Enum a) =>
+                (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> (a -> formula)
+             -> a -> formula
+ripplecarry1 x y c out n =
+  psimplify
+   (ripplecarry x y (\ i -> if i == 0 then true else c i) out n)
+
+mux :: forall formula. Combinable formula => formula -> formula -> formula -> formula
+mux sel in0 in1 = (((.~.) sel) .&. in0) .|. (sel .&. in1)
+
+offset :: forall t a. Num a => a -> (a -> t) -> a -> t
+offset n x i = x (n + i)
+
+carryselect :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> a -> a -> formula
+carryselect x y c0 c1 s0 s1 c s n k =
+  let k' = min n k in
+  let fm = ((ripplecarry0 x y c0 s0 k') .&. (ripplecarry1 x y c1 s1 k')) .&.
+           (((c k') .<=>. (mux (c 0) (c0 k') (c1 k'))) .&.
+            (conjoin (\ i -> (s i) .<=>. (mux (c 0) (s0 i) (s1 i)))
+                             (Set.fromList [0 .. (k' - 1)]))) in
+  if k' < k then fm else
+  fm .&. (carryselect
+          (offset k x) (offset k y) (offset k c0) (offset k c1)
+          (offset k s0) (offset k s1) (offset k c) (offset k s)
+          (n - k) k)
+
+-- ------------------------------------------------------------------------- 
+-- Equivalence problems for carry-select vs ripple carry adders.             
+-- ------------------------------------------------------------------------- 
+
+mk_adder_test :: forall formula a. (PropositionalFormula formula (Atom a), Ord a, Ord formula, Num a, Enum a) =>
+                 a -> a -> formula
+mk_adder_test n k =
+  let [x, y, c, s, c0, s0, c1, s1, c2, s2] =
+          map mk_index ["x", "y", "c", "s", "c0", "s0", "c1", "s1", "c2", "s2"] in
+  (((carryselect x y c0 c1 s0 s1 c s n k) .&.
+    ((.~.) (c 0))) .&.
+   (ripplecarry0 x y c2 s2 n)) .=>.
+  (((c n) .<=>. (c2 n)) .&.
+   (conjoin (\ i -> (s i) .<=>. (s2 i)) (Set.fromList [0 .. (n - 1)])))
+
+-- ------------------------------------------------------------------------- 
+-- Ripple carry stage that separates off the final result.                   
+--                                                                           
+--       UUUUUUUUUUUUUUUUUUUU  (u)                                           
+--    +  VVVVVVVVVVVVVVVVVVVV  (v)                                           
+--                                                                           
+--    = WWWWWWWWWWWWWWWWWWWW   (w)                                           
+--    +                     Z  (z)                                           
+-- ------------------------------------------------------------------------- 
+
+rippleshift :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+               (a -> formula)
+            -> (a -> formula)
+            -> (a -> formula)
+            -> formula
+            -> (a -> formula)
+            -> a -> formula
+rippleshift u v c z w n =
+  ripplecarry0 u v (\ i -> if i == n then w(n - 1) else c(i + 1))
+                   (\ i -> if i == 0 then z else w(i - 1)) n
+-- ------------------------------------------------------------------------- 
+-- Naive multiplier based on repeated ripple carry.                          
+-- ------------------------------------------------------------------------- 
+
+multiplier :: forall formula atomic a. (PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Enum a) =>
+              (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> a -> formula)
+           -> (a -> formula)
+           -> a
+           -> formula
+multiplier x u v out n =
+  if n == 1 then ((out 0) .<=>. (x 0 0)) .&. ((.~.)(out 1)) else
+  psimplify (((out 0) .<=>. (x 0 0)) .&.
+             ((rippleshift
+               (\ i -> if i == n - 1 then false else x 0 (i + 1))
+               (x 1) (v 2) (out 1) (u 2) n) .&.
+              (if n == 2 then ((out 2) .<=>. (u 2 0)) .&. ((out 3) .<=>. (u 2 1)) else
+                   conjoin (\ k -> rippleshift (u k) (x k) (v(k + 1)) (out k)
+                                   (if k == n - 1 then \ i -> out(n + i)
+                                    else u(k + 1)) n) (Set.fromList [2 .. (n - 1)]))))
+
+-- ------------------------------------------------------------------------- 
+-- Primality examples.                                                       
+-- For large examples, should use "num" instead of "int" in these functions. 
+-- ------------------------------------------------------------------------- 
+
+bitlength :: forall b a. (Num a, Num b, Bits b) => b -> a
+bitlength x = if x == 0 then 0 else 1 + bitlength (shiftR x 1);;
+
+bit :: forall a b. (Num a, Eq a, Bits b, Integral b) => a -> b -> Bool
+bit n x = if n == 0 then x `mod` 2 == 1 else bit (n - 1) (shiftR x 1)
+
+congruent_to :: forall formula atomic a b. (Bits b, PropositionalFormula formula atomic, Ord a, Ord formula, Num a, Integral b, Enum a) =>
+                (a -> formula) -> b -> a -> formula
+congruent_to x m n =
+  conjoin (\ i -> if bit i m then x i else (.~.)(x i))
+          (Set.fromList [0 .. (n - 1)])
+
+prime :: forall formula. (PropositionalFormula formula (Atom N), Ord formula) => N -> formula
+prime p =
+  let [x, y, out] = map mk_index ["x", "y", "out"] in
+  let m i j = (x i) .&. (y j)
+      [u, v] = map mk_index2 ["u", "v"] in
+  let (n :: Int) = bitlength p in
+  (.~.) (multiplier m u v out (n - 1) .&. congruent_to out p (max n (2 * n - 2)))
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+type F = Formula (Atom Int)
+
+deriving instance Show F
+
+{-
+instance Constants F where
+    fromBool True = 
+-}
+
+test03 :: Test
+test03 =
+    TestList [TestCase (assertEqual "tautology(prime 7)" True (tautology(prime 7 :: F))),
+              TestCase (assertEqual "tautology(prime 9)" False (tautology(prime 9 :: F))),
+              TestCase (assertEqual "tautology(prime 11)" True (tautology(prime 11 :: F)))]
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Resolution.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Resolution.hs
@@ -0,0 +1,1044 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Resolution
+    ( resolution1
+    , resolution2
+    , resolution3
+    , presolution
+    , matchAtomsEq
+    ) where
+
+import Data.Logic.Classes.Atom (Atom(match))
+import Data.Logic.Classes.Combine (Combination(..))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), zipFirstOrder)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(vt, foldTerm))
+import Data.Logic.Classes.Variable (Variable(prefix))
+import Data.Logic.Failing (failing, Failing(..))
+import Data.Logic.Harrison.FOL (fv, generalize, list_conj, list_disj, subst)
+import Data.Logic.Harrison.Lib (allnonemptysubsets, allpairs, allsubsets, apply, defined, setAll, setAny, settryfind, (|->))
+import Data.Logic.Harrison.Normal (simpcnf, simpdnf, trivial)
+import Data.Logic.Harrison.Skolem (askolemize, pnf, SkolemT, specialize)
+import Data.Logic.Harrison.Tableaux (unify_literals)
+import Data.Logic.Harrison.Unif (solve)
+import qualified Data.Map as Map (empty, fromList, Map)
+import Data.Maybe (fromMaybe)
+import qualified Data.Set as Set (difference, empty, filter, fold, insert, map, member, minView, null, partition, Set, singleton, toList, union)
+
+-- ========================================================================= 
+-- Resolution.                                                               
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- MGU of a set of literals.                                                 
+-- ------------------------------------------------------------------------- 
+
+mgu :: forall lit atom term v f. (Literal lit atom, Term term v f, Atom atom term v) =>
+       Set.Set lit -> Map.Map v term -> Failing (Map.Map v term)
+mgu l env =
+    case Set.minView l of
+      Just (a, rest) ->
+          case Set.minView rest of
+            Just (b, _) -> unify_literals env a b >>= mgu rest
+            _ -> Success (solve env)
+      _ -> Success (solve env)
+
+unifiable :: (Literal lit atom, Term term v f, Atom atom term v) =>
+             lit -> lit -> Bool
+unifiable p q = failing (const False) (const True) (unify_literals Map.empty p q)
+
+-- ------------------------------------------------------------------------- 
+-- Rename a clause.                                                          
+-- ------------------------------------------------------------------------- 
+
+rename :: (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+          (v -> v) -> Set.Set fof -> Set.Set fof
+rename pfx cls =
+    Set.map (subst (Map.fromList (zip fvs vvs))) cls
+    where
+      -- fvs :: [v]
+      fvs = Set.toList (fv (list_disj cls))
+      -- vvs :: [term]
+      vvs = map (vt . pfx) fvs
+
+-- ------------------------------------------------------------------------- 
+-- General resolution rule, incorporating factoring as in Robinson's paper.  
+-- ------------------------------------------------------------------------- 
+
+resolvents :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+              Set.Set fof -> Set.Set fof -> fof -> Set.Set fof -> Set.Set fof
+resolvents cl1 cl2 p acc =
+    if Set.null ps2 then acc else Set.fold doPair acc pairs
+    where
+      doPair (s1,s2) sof =
+          case mgu (Set.union s1 (Set.map (.~.) s2)) Map.empty of
+            Success mp -> Set.union (Set.map (subst mp) (Set.union (Set.difference cl1 s1) (Set.difference cl2 s2))) sof
+            Failure _ -> sof
+      -- pairs :: Set.Set (Set.Set fof, Set.Set fof)
+      pairs = allpairs (,) (Set.map (Set.insert p) (allsubsets ps1)) (allnonemptysubsets ps2)
+      -- ps1 :: Set.Set fof
+      ps1 = Set.filter (\ q -> q /= p && unifiable p q) cl1
+      -- ps2 :: Set.Set fof
+      ps2 = Set.filter (unifiable ((.~.) p)) cl2
+
+resolve_clauses :: forall fof atom v term f.
+                   (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                   Set.Set fof -> Set.Set fof -> Set.Set fof
+resolve_clauses cls1 cls2 =
+    let cls1' = rename (prefix "x") cls1
+        cls2' = rename (prefix "y") cls2 in
+    Set.fold (resolvents cls1' cls2') Set.empty cls1'
+
+-- ------------------------------------------------------------------------- 
+-- Basic "Argonne" loop.                                                     
+-- ------------------------------------------------------------------------- 
+
+resloop1 :: forall atom v term f fof. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+resloop1 used unused =
+    maybe (Failure ["No proof found"]) step (Set.minView unused)
+    where
+      step (cl, ros) =
+          if Set.member Set.empty news then return True else resloop1 used' (Set.union ros news)
+          where
+            used' = Set.insert cl used
+            -- resolve_clauses is not in the Failing monad, so setmapfilter isn't appropriate.
+            news = Set.fold Set.insert Set.empty ({-setmapfilter-} Set.map (resolve_clauses cl) used')
+
+pure_resolution1 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution1 fm = resloop1 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution1 :: forall m fof term f atom v.
+               (Literal fof atom,
+                FirstOrderFormula fof atom v,
+                PropositionalFormula fof atom,
+                Term term v f,
+                Atom atom term v,
+                Ord fof,
+                Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution1 fm = askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution1 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Matching of terms and literals.                                           
+-- ------------------------------------------------------------------------- 
+
+term_match :: forall term v f. (Term term v f) => Map.Map v term -> [(term, term)] -> Failing (Map.Map v term)
+term_match env [] = Success env
+term_match env ((p, q) : oth) =
+    foldTerm v fn p
+    where
+      v x = if not (defined env x)
+            then term_match ((x |-> q) env) oth
+            else if apply env x == Just q
+                 then term_match env oth
+                 else Failure ["term_match"]
+      fn f fa =
+          foldTerm v' fn' q
+          where
+            fn' g ga | f == g && length fa == length ga = term_match env (zip fa ga ++ oth)
+            fn' _ _ = Failure ["term_match"]
+            v' _ = Failure ["term_match"]
+{-
+  case eqs of
+    [] -> Success env
+    (Fn f fa, Fn g ga) : oth
+        | f == g && length fa == length ga ->
+           term_match env (zip fa ga ++ oth)
+    (Var x, t) : oth ->
+        if not (defined env x) then term_match ((x |-> t) env) oth
+        else if apply env x == t then term_match env oth
+        else Failure ["term_match"]
+    _ -> Failure ["term_match"]
+-}
+
+match_literals :: forall term f v fof atom. (FirstOrderFormula fof atom v, Atom atom term v, Term term v f) =>
+                  Map.Map v term -> fof -> fof -> Failing (Map.Map v term)
+match_literals env t1 t2 =
+    fromMaybe err (zipFirstOrder qu co tf at t1 t2)
+    where
+      qu _ _ _ _ _ _ = Nothing
+      co ((:~:) p) ((:~:) q) = Just $ match_literals env p q
+      co _ _ = Nothing
+      tf a b = if a == b then Just (Success env) else Nothing
+      at a1 a2 = Just (match env a1 a2)
+      err = Failure ["match_literals"]
+
+-- Identical to unifyAtomsEq except calls term_match instead of unify.
+matchAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+matchAtomsEq env a1 a2 =
+    fromMaybe err (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p ts1 q ts2 =
+          if p == q && length ts1 == length ts2
+          then Just (term_match env (zip ts1 ts2))
+          else Nothing
+      tf p q = if p == q then Just (Success env) else Nothing
+      eq pl pr ql qr = Just (term_match env [(pl, ql), (pr, qr)])
+      err = Failure ["matchAtomsEq"]
+
+{-
+    case tmp of
+      (Atom (R p a1), Atom(R q a2)) -> term_match env [(Fn p a1, Fn q a2)]
+      (Not (Atom (R p a1)), Not (Atom (R q a2))) -> term_match env [(Fn p a1, Fn q a2)]
+      _ -> Failure ["match_literals"]
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Test for subsumption                                                      
+-- ------------------------------------------------------------------------- 
+
+subsumes_clause :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v) =>
+                   Set.Set fof -> Set.Set fof -> Bool
+subsumes_clause cls1 cls2 =
+    failing (const False) (const True) (subsume Map.empty cls1)
+    where
+      -- subsume :: Map.Map v term -> Set.Set fof -> Failing (Map.Map v term)
+      subsume env cls =
+          case Set.minView cls of
+            Nothing -> Success env
+            Just (l1, clt) -> settryfind (\ l2 -> case (match_literals env l1 l2) of
+                                                    Success env' -> subsume env' clt
+                                                    Failure msgs -> Failure msgs) cls2
+-- ------------------------------------------------------------------------- 
+-- With deletion of tautologies and bi-subsumption with "unused".            
+-- ------------------------------------------------------------------------- 
+
+replace :: forall term f v fof atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+           Set.Set fof
+        -> Set.Set (Set.Set fof)
+        -> Set.Set (Set.Set fof)
+replace cl st =
+    case Set.minView st of
+      Nothing -> Set.singleton cl
+      Just (c, st') -> if subsumes_clause cl c
+                       then Set.insert cl st'
+                       else Set.insert c (replace cl st')
+
+incorporate :: forall fof term f v atom. (FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+               Set.Set fof
+            -> Set.Set fof
+            -> Set.Set (Set.Set fof)
+            -> Set.Set (Set.Set fof)
+incorporate gcl cl unused =
+    if trivial cl || setAny (\ c -> subsumes_clause c cl) (Set.insert gcl unused)
+    then unused
+    else replace cl unused
+
+resloop2 :: forall fof term f v atom. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof)
+         -> Set.Set (Set.Set fof)
+         -> Failing Bool
+resloop2 used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl {- :: Set.Set fof-}, ros {- :: Set.Set (Set.Set fof) -}) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = {-Set.fold Set.union Set.empty-} (Set.map (resolve_clauses cl) used') in
+          if Set.member Set.empty news then return True else resloop2 used' (Set.fold (incorporate cl) ros news)
+
+pure_resolution2 :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution2 fm = resloop2 Set.empty (simpcnf (specialize (pnf fm)))
+
+resolution2 :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution2 fm = askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_resolution2 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Positive (P1) resolution.                                                 
+-- ------------------------------------------------------------------------- 
+
+presolve_clauses :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    Set.Set fof -> Set.Set fof -> Set.Set fof
+presolve_clauses cls1 cls2 =
+    if setAll positive cls1 || setAll positive cls2
+    then resolve_clauses cls1 cls2
+    else Set.empty
+
+presloop :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+            Set.Set (Set.Set fof) -> Set.Set (Set.Set fof) -> Failing Bool
+presloop used unused =
+    case Set.minView unused of
+      Nothing -> Failure ["No proof found"]
+      Just (cl, ros) ->
+          -- print_string(string_of_int(length used) ^ " used; "^ string_of_int(length unused) ^ " unused.");
+          -- print_newline();
+          let used' = Set.insert cl used in
+          let news = Set.map (presolve_clauses cl) used' in
+          if Set.member Set.empty news
+          then Success True
+          else presloop used' (Set.fold (incorporate cl) ros news)
+
+pure_presolution :: forall fof atom v term f. (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_presolution fm = presloop Set.empty (simpcnf (specialize (pnf fm)))
+
+presolution :: forall fof atom term v f m.
+               (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+presolution fm =
+    askolemize ((.~.) (generalize fm)) >>= return . Set.map (pure_presolution . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+
+-- ------------------------------------------------------------------------- 
+-- Introduce a set-of-support restriction.                                   
+-- ------------------------------------------------------------------------- 
+
+pure_resolution3 :: (Literal fof atom, FirstOrderFormula fof atom v, Term term v f, Atom atom term v, Ord fof) =>
+                    fof -> Failing Bool
+pure_resolution3 fm =
+    uncurry resloop2 (Set.partition (setAny positive) (simpcnf (specialize (pnf fm))))
+
+resolution3 :: forall fof atom term v f m. (Literal fof atom, FirstOrderFormula fof atom v, PropositionalFormula fof atom, Term term v f, Atom atom term v, Ord fof, Monad m) =>
+               fof -> SkolemT v term m (Set.Set (Failing Bool))
+resolution3 fm =
+    askolemize ((.~.)(generalize fm)) >>= return . Set.map (pure_resolution3 . list_conj) . (simpdnf :: fof -> Set.Set (Set.Set fof))
+{-
+-- ------------------------------------------------------------------------- 
+-- The Pelletier examples again.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time presolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time presolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time presolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time presolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time presolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time presolution
+ <<p \/ ~p>>;;
+
+let p7 = time presolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time presolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time presolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time presolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time presolution
+ <<p <=> p>>;;
+
+let p12 = time presolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time presolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time presolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time presolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time presolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time presolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Monadic Predicate Logic.                                                  
+-- ------------------------------------------------------------------------- 
+
+let p18 = time presolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time presolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time presolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w))
+   ==> (exists x y. P(x) /\ Q(y)) ==> (exists z. R(z))>>;;
+
+let p21 = time presolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P)
+   ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time presolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time presolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time presolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time presolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time presolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time presolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time presolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time presolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time presolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\
+   (forall x. (G(x) ==> ~U(x)) ==> P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time presolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time presolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time presolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time presolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+    ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+    ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time presolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+--  Full predicate logic (without Identity and Functions)                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time presolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time presolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+{- ** This one seems too slow
+
+let p38 = time presolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ** -}
+
+let p39 = time presolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time presolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time presolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+{- ** Also very slow
+
+let p42 = time presolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ** -}
+
+{- ** and this one too..
+
+let p43 = time presolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ** -}
+
+let p44 = time presolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+{- ** and this...
+
+let p45 = time presolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ** -}
+
+{- ** and this
+
+let p46 = time presolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Manthey and Bry, CADE-9.                                     
+-- ------------------------------------------------------------------------- 
+
+let p55 = time presolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time presolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time presolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time presolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time presolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+let gilmore_1 = time presolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time presolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time presolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time presolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time presolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time presolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time presolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+{- ** This one still isn't easy!
+
+let gilmore_9 = time presolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ** -}
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time presolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+*********** -}
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Example                                                                   
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let gilmore_1 = resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletiers yet again.                                                     
+-- ------------------------------------------------------------------------- 
+
+{- ************
+
+let p1 = time resolution
+ <<p ==> q <=> ~q ==> ~p>>;;
+
+let p2 = time resolution
+ <<~ ~p <=> p>>;;
+
+let p3 = time resolution
+ <<~(p ==> q) ==> q ==> p>>;;
+
+let p4 = time resolution
+ <<~p ==> q <=> ~q ==> p>>;;
+
+let p5 = time resolution
+ <<(p \/ q ==> p \/ r) ==> p \/ (q ==> r)>>;;
+
+let p6 = time resolution
+ <<p \/ ~p>>;;
+
+let p7 = time resolution
+ <<p \/ ~ ~ ~p>>;;
+
+let p8 = time resolution
+ <<((p ==> q) ==> p) ==> p>>;;
+
+let p9 = time resolution
+ <<(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)>>;;
+
+let p10 = time resolution
+ <<(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)>>;;
+
+let p11 = time resolution
+ <<p <=> p>>;;
+
+let p12 = time resolution
+ <<((p <=> q) <=> r) <=> (p <=> (q <=> r))>>;;
+
+let p13 = time resolution
+ <<p \/ q /\ r <=> (p \/ q) /\ (p \/ r)>>;;
+
+let p14 = time resolution
+ <<(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)>>;;
+
+let p15 = time resolution
+ <<p ==> q <=> ~p \/ q>>;;
+
+let p16 = time resolution
+ <<(p ==> q) \/ (q ==> p)>>;;
+
+let p17 = time resolution
+ <<p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* Monadic Predicate Logic.                                                  *)
+(* ------------------------------------------------------------------------- *)
+
+let p18 = time resolution
+ <<exists y. forall x. P(y) ==> P(x)>>;;
+
+let p19 = time resolution
+ <<exists x. forall y z. (P(y) ==> Q(z)) ==> P(x) ==> Q(x)>>;;
+
+let p20 = time resolution
+ <<(forall x y. exists z. forall w. P(x) /\ Q(y) ==> R(z) /\ U(w)) ==>
+   (exists x y. P(x) /\ Q(y)) ==>
+   (exists z. R(z))>>;;
+
+let p21 = time resolution
+ <<(exists x. P ==> Q(x)) /\ (exists x. Q(x) ==> P) ==> (exists x. P <=> Q(x))>>;;
+
+let p22 = time resolution
+ <<(forall x. P <=> Q(x)) ==> (P <=> (forall x. Q(x)))>>;;
+
+let p23 = time resolution
+ <<(forall x. P \/ Q(x)) <=> P \/ (forall x. Q(x))>>;;
+
+let p24 = time resolution
+ <<~(exists x. U(x) /\ Q(x)) /\
+   (forall x. P(x) ==> Q(x) \/ R(x)) /\
+   ~(exists x. P(x) ==> (exists x. Q(x))) /\
+   (forall x. Q(x) /\ R(x) ==> U(x)) ==>
+   (exists x. P(x) /\ R(x))>>;;
+
+let p25 = time resolution
+ <<(exists x. P(x)) /\
+   (forall x. U(x) ==> ~G(x) /\ R(x)) /\
+   (forall x. P(x) ==> G(x) /\ U(x)) /\
+   ((forall x. P(x) ==> Q(x)) \/ (exists x. Q(x) /\ P(x))) ==>
+   (exists x. Q(x) /\ P(x))>>;;
+
+let p26 = time resolution
+ <<((exists x. P(x)) <=> (exists x. Q(x))) /\
+   (forall x y. P(x) /\ Q(y) ==> (R(x) <=> U(y))) ==>
+   ((forall x. P(x) ==> R(x)) <=> (forall x. Q(x) ==> U(x)))>>;;
+
+let p27 = time resolution
+ <<(exists x. P(x) /\ ~Q(x)) /\
+   (forall x. P(x) ==> R(x)) /\
+   (forall x. U(x) /\ V(x) ==> P(x)) /\
+   (exists x. R(x) /\ ~Q(x)) ==>
+   (forall x. U(x) ==> ~R(x)) ==>
+   (forall x. U(x) ==> ~V(x))>>;;
+
+let p28 = time resolution
+ <<(forall x. P(x) ==> (forall x. Q(x))) /\
+   ((forall x. Q(x) \/ R(x)) ==> (exists x. Q(x) /\ R(x))) /\
+   ((exists x. R(x)) ==> (forall x. L(x) ==> M(x))) ==>
+   (forall x. P(x) /\ L(x) ==> M(x))>>;;
+
+let p29 = time resolution
+ <<(exists x. P(x)) /\ (exists x. G(x)) ==>
+   ((forall x. P(x) ==> H(x)) /\ (forall x. G(x) ==> J(x)) <=>
+    (forall x y. P(x) /\ G(y) ==> H(x) /\ J(y)))>>;;
+
+let p30 = time resolution
+ <<(forall x. P(x) \/ G(x) ==> ~H(x)) /\ (forall x. (G(x) ==> ~U(x)) ==>
+     P(x) /\ H(x)) ==>
+   (forall x. U(x))>>;;
+
+let p31 = time resolution
+ <<~(exists x. P(x) /\ (G(x) \/ H(x))) /\ (exists x. Q(x) /\ P(x)) /\
+   (forall x. ~H(x) ==> J(x)) ==>
+   (exists x. Q(x) /\ J(x))>>;;
+
+let p32 = time resolution
+ <<(forall x. P(x) /\ (G(x) \/ H(x)) ==> Q(x)) /\
+   (forall x. Q(x) /\ H(x) ==> J(x)) /\
+   (forall x. R(x) ==> H(x)) ==>
+   (forall x. P(x) /\ R(x) ==> J(x))>>;;
+
+let p33 = time resolution
+ <<(forall x. P(a) /\ (P(x) ==> P(b)) ==> P(c)) <=>
+   (forall x. P(a) ==> P(x) \/ P(c)) /\ (P(a) ==> P(b) ==> P(c))>>;;
+
+let p34 = time resolution
+ <<((exists x. forall y. P(x) <=> P(y)) <=>
+   ((exists x. Q(x)) <=> (forall y. Q(y)))) <=>
+   ((exists x. forall y. Q(x) <=> Q(y)) <=>
+  ((exists x. P(x)) <=> (forall y. P(y))))>>;;
+
+let p35 = time resolution
+ <<exists x y. P(x,y) ==> (forall x y. P(x,y))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(*  Full predicate logic (without Identity and Functions)                    *)
+(* ------------------------------------------------------------------------- *)
+
+let p36 = time resolution
+ <<(forall x. exists y. P(x,y)) /\
+   (forall x. exists y. G(x,y)) /\
+   (forall x y. P(x,y) \/ G(x,y)
+   ==> (forall z. P(y,z) \/ G(y,z) ==> H(x,z)))
+       ==> (forall x. exists y. H(x,y))>>;;
+
+let p37 = time resolution
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) ==> P(y,w)) /\ P(y,z) /\
+     (P(y,w) ==> (exists u. Q(u,w)))) /\
+   (forall x z. ~P(x,z) ==> (exists y. Q(y,z))) /\
+   ((exists x y. Q(x,y)) ==> (forall x. R(x,x))) ==>
+   (forall x. exists y. R(x,y))>>;;
+
+(*** This one seems too slow
+
+let p38 = time resolution
+ <<(forall x.
+     P(a) /\ (P(x) ==> (exists y. P(y) /\ R(x,y))) ==>
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))) <=>
+   (forall x.
+     (~P(a) \/ P(x) \/ (exists z w. P(z) /\ R(x,w) /\ R(w,z))) /\
+     (~P(a) \/ ~(exists y. P(y) /\ R(x,y)) \/
+     (exists z w. P(z) /\ R(x,w) /\ R(w,z))))>>;;
+
+ ***)
+
+let p39 = time resolution
+ <<~(exists x. forall y. P(y,x) <=> ~P(y,y))>>;;
+
+let p40 = time resolution
+ <<(exists y. forall x. P(x,y) <=> P(x,x))
+  ==> ~(forall x. exists y. forall z. P(z,y) <=> ~P(z,x))>>;;
+
+let p41 = time resolution
+ <<(forall z. exists y. forall x. P(x,y) <=> P(x,z) /\ ~P(x,x))
+  ==> ~(exists z. forall x. P(x,z))>>;;
+
+(*** Also very slow
+
+let p42 = time resolution
+ <<~(exists y. forall x. P(x,y) <=> ~(exists z. P(x,z) /\ P(z,x)))>>;;
+
+ ***)
+
+(*** and this one too..
+
+let p43 = time resolution
+ <<(forall x y. Q(x,y) <=> forall z. P(z,x) <=> P(z,y))
+   ==> forall x y. Q(x,y) <=> Q(y,x)>>;;
+
+ ***)
+
+let p44 = time resolution
+ <<(forall x. P(x) ==> (exists y. G(y) /\ H(x,y)) /\
+   (exists y. G(y) /\ ~H(x,y))) /\
+   (exists x. J(x) /\ (forall y. G(y) ==> H(x,y))) ==>
+   (exists x. J(x) /\ ~P(x))>>;;
+
+(*** and this...
+
+let p45 = time resolution
+ <<(forall x.
+     P(x) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y)) ==>
+       (forall y. G(y) /\ H(x,y) ==> R(y))) /\
+   ~(exists y. L(y) /\ R(y)) /\
+   (exists x. P(x) /\ (forall y. H(x,y) ==>
+     L(y)) /\ (forall y. G(y) /\ H(x,y) ==> J(x,y))) ==>
+   (exists x. P(x) /\ ~(exists y. G(y) /\ H(x,y)))>>;;
+
+ ***)
+
+(*** and this
+
+let p46 = time resolution
+ <<(forall x. P(x) /\ (forall y. P(y) /\ H(y,x) ==> G(y)) ==> G(x)) /\
+   ((exists x. P(x) /\ ~G(x)) ==>
+    (exists x. P(x) /\ ~G(x) /\
+               (forall y. P(y) /\ ~G(y) ==> J(x,y)))) /\
+   (forall x y. P(x) /\ P(y) /\ H(x,y) ==> ~J(y,x)) ==>
+   (forall x. P(x) ==> G(x))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Manthey and Bry, CADE-9.                                     *)
+(* ------------------------------------------------------------------------- *)
+
+let p55 = time resolution
+ <<lives(agatha) /\ lives(butler) /\ lives(charles) /\
+   (killed(agatha,agatha) \/ killed(butler,agatha) \/
+    killed(charles,agatha)) /\
+   (forall x y. killed(x,y) ==> hates(x,y) /\ ~richer(x,y)) /\
+   (forall x. hates(agatha,x) ==> ~hates(charles,x)) /\
+   (hates(agatha,agatha) /\ hates(agatha,charles)) /\
+   (forall x. lives(x) /\ ~richer(x,agatha) ==> hates(butler,x)) /\
+   (forall x. hates(agatha,x) ==> hates(butler,x)) /\
+   (forall x. ~hates(x,agatha) \/ ~hates(x,butler) \/ ~hates(x,charles))
+   ==> killed(agatha,agatha) /\
+       ~killed(butler,agatha) /\
+       ~killed(charles,agatha)>>;;
+
+let p57 = time resolution
+ <<P(f((a),b),f(b,c)) /\
+   P(f(b,c),f(a,c)) /\
+   (forall (x) y z. P(x,y) /\ P(y,z) ==> P(x,z))
+   ==> P(f(a,b),f(a,c))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* See info-hol, circa 1500.                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let p58 = time resolution
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P(x) /\ Q(y)) ==> ((P(v) \/ R(w))  /\ (R(z) ==> Q(v))))>>;;
+
+let p59 = time resolution
+ <<(forall x. P(x) <=> ~P(f(x))) ==> (exists x. P(x) /\ ~P(f(x)))>>;;
+
+let p60 = time resolution
+ <<forall x. P(x,f(x)) <=>
+            exists y. (forall z. P(z,y) ==> P(z,f(x))) /\ P(x,y)>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* From Gilmore's classic paper.                                             *)
+(* ------------------------------------------------------------------------- *)
+
+let gilmore_1 = time resolution
+ <<exists x. forall y z.
+      ((F(y) ==> G(y)) <=> F(x)) /\
+      ((F(y) ==> H(y)) <=> G(x)) /\
+      (((F(y) ==> G(y)) ==> H(y)) <=> H(x))
+      ==> F(z) /\ G(z) /\ H(z)>>;;
+
+(*** This is not valid, according to Gilmore
+
+let gilmore_2 = time resolution
+ <<exists x y. forall z.
+        (F(x,z) <=> F(z,y)) /\ (F(z,y) <=> F(z,z)) /\ (F(x,y) <=> F(y,x))
+        ==> (F(x,y) <=> F(x,z))>>;;
+
+ ***)
+
+let gilmore_3 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> H(x))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> H(z)) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+let gilmore_4 = time resolution
+ <<exists x y. forall z.
+        (F(x,y) ==> F(y,z) /\ F(z,z)) /\
+        (F(x,y) /\ G(x,y) ==> G(x,z) /\ G(z,z))>>;;
+
+let gilmore_5 = time resolution
+ <<(forall x. exists y. F(x,y) \/ F(y,x)) /\
+   (forall x y. F(y,x) ==> F(y,y))
+   ==> exists z. F(z,z)>>;;
+
+let gilmore_6 = time resolution
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) ==> G(v,u) /\ G(u,x))
+        ==> (exists u. forall v. F(u,y) ==> G(v,u) /\ G(u,y)) \/
+            (forall u v. exists w. G(v,u) \/ H(w,y,u) ==> G(u,w))>>;;
+
+let gilmore_7 = time resolution
+ <<(forall x. K(x) ==> exists y. L(y) /\ (F(x,y) ==> G(x,y))) /\
+   (exists z. K(z) /\ forall u. L(u) ==> F(z,u))
+   ==> exists v w. K(v) /\ L(w) /\ G(v,w)>>;;
+
+let gilmore_8 = time resolution
+ <<exists x. forall y z.
+        ((F(y,z) ==> (G(y) ==> (forall u. exists v. H(u,v,x)))) ==> F(x,x)) /\
+        ((F(z,x) ==> G(x)) ==> (forall u. exists v. H(u,v,z))) /\
+        F(x,y)
+        ==> F(z,z)>>;;
+
+(*** This one still isn't easy!
+
+let gilmore_9 = time resolution
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x))
+          ==> (forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))) /\
+        ((forall u. exists v. F(x,u,v) /\ G(y,u) /\ ~H(x,y))
+         ==> ~(forall u. exists v. F(x,u,v) /\ G(z,u) /\ ~H(x,z))
+             ==> (forall u. exists v. F(y,u,v) /\ G(y,u) /\ ~H(y,x)) /\
+                 (forall u. exists v. F(z,u,v) /\ G(y,u) /\ ~H(z,y)))>>;;
+
+ ***)
+
+(* ------------------------------------------------------------------------- *)
+(* Example from Davis-Putnam papers where Gilmore procedure is poor.         *)
+(* ------------------------------------------------------------------------- *)
+
+let davis_putnam_example = time resolution
+ <<exists x. exists y. forall z.
+        (F(x,y) ==> (F(y,z) /\ F(z,z))) /\
+        ((F(x,y) /\ G(x,y)) ==> (G(x,z) /\ G(z,z)))>>;;
+
+(* ------------------------------------------------------------------------- *)
+(* The (in)famous Los problem.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+let los = time resolution
+ <<(forall x y z. P(x,y) ==> P(y,z) ==> P(x,z)) /\
+   (forall x y z. Q(x,y) ==> Q(y,z) ==> Q(x,z)) /\
+   (forall x y. Q(x,y) ==> Q(y,x)) /\
+   (forall x y. P(x,y) \/ Q(x,y))
+   ==> (forall x y. P(x,y)) \/ (forall x y. Q(x,y))>>;;
+
+************* -}
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Skolem.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Skolem.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Skolem.hs
@@ -0,0 +1,355 @@
+{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Skolem
+    ( simplify
+    -- , simplify'
+    , lsimplify
+    , nnf
+    -- , nnf'
+    , pnf
+    -- , pnf'
+    , functions
+    -- , functions'
+    , SkolemT
+    , Skolem
+    , runSkolem
+    , runSkolemT
+    , specialize
+    , skolemize
+    -- , literal
+    , askolemize
+    , skolemNormalForm
+    -- , prenex'
+    , skolem
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (StateT(runStateT))
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(exists, for_all, foldFirstOrder), quant, Quant(..), toPropositional)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal(foldLiteral))
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import qualified Data.Logic.Classes.Skolem as C (Skolem(toSkolem))
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Classes.Variable (Variable(variant))
+import Data.Logic.Harrison.FOL (fv, subst)
+import Data.Logic.Harrison.Lib ((|=>))
+import qualified Data.Map as Map (singleton)
+import qualified Data.Set as Set (empty, member, Set, toAscList, union)
+
+-- =========================================================================
+-- Prenex and Skolem normal forms.                                           
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Routine simplification. Like "psimplify" but with quantifier clauses.     
+-- ------------------------------------------------------------------------- 
+
+simplify1 :: (FirstOrderFormula fof atom v,
+              -- Formula fof term v,
+              Atom atom term v,
+              Term term v f) => fof -> fof
+simplify1 fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu _ x p = if Set.member x (fv p) then fm else p
+      co ((:~:) p) = foldFirstOrder (\ _ _ _ -> fm) nco (fromBool . not) (\ _ -> fm) p
+      co (BinOp l op r) = simplifyBinop l op r
+      nco ((:~:) p) = p
+      nco _ = fm
+      tf = fromBool
+      at _ = fm
+
+simplifyBinop :: forall p. (Constants p, Combinable p) => p -> BinOp -> p -> p
+simplifyBinop l op r =
+    case (asBool l, op, asBool r) of
+      (Just True,  (:&:), _         ) -> r
+      (Just False, (:&:), _         ) -> false
+      (_,          (:&:), Just True ) -> l
+      (_,          (:&:), Just False) -> false
+      (Just True,  (:|:), _         ) -> true
+      (Just False, (:|:), _         ) -> r
+      (_,          (:|:), Just True ) -> true
+      (_,          (:|:), Just False) -> l
+      (Just True,  (:=>:), _         ) -> r
+      (Just False, (:=>:), _         ) -> true
+      (_,          (:=>:), Just True ) -> true
+      (_,          (:=>:), Just False) -> (.~.) l
+      (Just False, (:<=>:), Just False) -> true
+      (Just True,  (:<=>:), _         ) -> r
+      (Just False, (:<=>:), _         ) -> (.~.) r
+      (_,          (:<=>:), Just True ) -> l
+      (_,          (:<=>:), Just False) -> (.~.) l
+      _ -> binop l op r
+
+simplify :: (FirstOrderFormula fof atom v,
+             Atom atom term v,
+             Term term v f) => fof -> fof
+simplify fm =
+    foldFirstOrder qu co tf at fm
+    where
+      qu op x fm' = simplify1 (quant op x (simplify fm'))
+      co ((:~:) fm') = simplify1 ((.~.) (simplify fm'))
+      co (BinOp fm1 op fm2) = simplify1 (binop (simplify fm1) op (simplify fm2))
+      tf = fromBool
+      at _ = fm
+
+-- | Just looks for double negatives and negated constants.
+lsimplify :: Literal lit atom => lit -> lit
+lsimplify fm = foldLiteral (lsimplify1 . (.~.) . lsimplify) fromBool (const fm) fm
+
+lsimplify1 :: Literal lit atom => lit -> lit
+lsimplify1 fm = foldLiteral (foldLiteral id (fromBool . not) (const fm)) fromBool (const fm) fm
+
+
+-- ------------------------------------------------------------------------- 
+-- Negation normal form.                                                     
+-- ------------------------------------------------------------------------- 
+
+nnf :: FirstOrderFormula formula atom v => formula -> formula
+nnf fm =
+    foldFirstOrder nnfQuant nnfCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      nnfQuant op v p = quant op v (nnf p)
+      nnfCombine ((:~:) p) = foldFirstOrder nnfNotQuant nnfNotCombine (fromBool . not) (\ _ -> fm) p
+      nnfCombine (BinOp p (:=>:) q) = nnf ((.~.) p) .|. (nnf q)
+      nnfCombine (BinOp p (:<=>:) q) =  (nnf p .&. nnf q) .|. (nnf ((.~.) p) .&. nnf ((.~.) q))
+      nnfCombine (BinOp p (:&:) q) = nnf p .&. nnf q
+      nnfCombine (BinOp p (:|:) q) = nnf p .|. nnf q
+      nnfNotQuant Forall v p = exists v (nnf ((.~.) p))
+      nnfNotQuant Exists v p = for_all v (nnf ((.~.) p))
+      nnfNotCombine ((:~:) p) = nnf p
+      nnfNotCombine (BinOp p (:&:) q) = nnf ((.~.) p) .|. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:|:) q) = nnf ((.~.) p) .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:=>:) q) = nnf p .&. nnf ((.~.) q)
+      nnfNotCombine (BinOp p (:<=>:) q) = (nnf p .&. nnf ((.~.) q)) .|. nnf ((.~.) p) .&. nnf q
+
+-- ------------------------------------------------------------------------- 
+-- Prenex normal form.                                                       
+-- ------------------------------------------------------------------------- 
+
+pullQuants :: forall formula atom v term f. (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+              formula -> formula
+pullQuants fm =
+    foldFirstOrder (\ _ _ _ -> fm) pullQuantsCombine (\ _ -> fm) (\ _ -> fm) fm
+    where
+      getQuant = foldFirstOrder (\ op v f -> Just (op, v, f)) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing)
+      pullQuantsCombine ((:~:) _) = fm
+      pullQuantsCombine (BinOp l op r) = 
+          case (getQuant l, op, getQuant r) of
+            (Just (Forall, vl, l'), (:&:), Just (Forall, vr, r')) -> pullq True  True  fm for_all (.&.) vl vr l' r'
+            (Just (Exists, vl, l'), (:|:), Just (Exists, vr, r')) -> pullq True  True  fm exists  (.|.) vl vr l' r'
+            (Just (Forall, vl, l'), (:&:), _)                     -> pullq True  False fm for_all (.&.) vl vl l' r
+            (_,                     (:&:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.&.) vr vr l  r'
+            (Just (Forall, vl, l'), (:|:), _)                     -> pullq True  False fm for_all (.|.) vl vl l' r
+            (_,                     (:|:), Just (Forall, vr, r')) -> pullq False True  fm for_all (.|.) vr vr l  r'
+            (Just (Exists, vl, l'), (:&:), _)                     -> pullq True  False fm exists  (.&.) vl vl l' r
+            (_,                     (:&:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.&.) vr vr l  r'
+            (Just (Exists, vl, l'), (:|:), _)                     -> pullq True  False fm exists  (.|.) vl vl l' r
+            (_,                     (:|:), Just (Exists, vr, r')) -> pullq False True  fm exists  (.|.) vr vr l  r'
+            _                                                     -> fm
+
+-- |Helper function to rename variables when we want to enclose a
+-- formula containing a free occurrence of that variable a quantifier
+-- that quantifies it.
+pullq :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) =>
+         Bool -> Bool
+      -> formula
+      -> (v -> formula -> formula)
+      -> (formula -> formula -> formula)
+      -> v -> v
+      -> formula -> formula
+      -> formula
+pullq l r fm mkq op x y p q =
+    let z = variant x (fv fm)
+        p' = if l then subst (x |=> vt z) p else p
+        q' = if r then subst (y |=> vt z) q else q
+        fm' = pullQuants (op p' q') in
+    mkq z fm'
+
+-- |Recursivly apply pullQuants anywhere a quantifier might not be
+-- leftmost.
+prenex :: (FirstOrderFormula formula atom v, {-Formula formula term v,-} Atom atom term v, Term term v f) =>
+          formula -> formula 
+prenex fm =
+    foldFirstOrder qu co (\ _ -> fm) (\ _ -> fm) fm
+    where
+      qu op x p = quant op x (prenex p)
+      co (BinOp l (:&:) r) = pullQuants (prenex l .&. prenex r)
+      co (BinOp l (:|:) r) = pullQuants (prenex l .|. prenex r)
+      co _ = fm
+
+-- |Convert to Prenex normal form, with all quantifiers at the left.
+pnf :: (FirstOrderFormula formula atom v, Atom atom term v, Term term v f) => formula -> formula
+pnf = prenex . nnf . simplify
+
+-- ------------------------------------------------------------------------- 
+-- Get the functions in a term and formula.                                  
+-- ------------------------------------------------------------------------- 
+
+-- FIXME: the function parameter should be a method in the Atom class,
+-- but we need to add a type parameter f to it first.
+functions :: forall formula atom f. (Formula formula atom, Ord f) => (atom -> Set.Set (f, Int)) -> formula -> Set.Set (f, Int)
+functions fa fm = foldAtoms (\ s a -> Set.union s (fa a)) Set.empty fm
+
+-- ------------------------------------------------------------------------- 
+-- State monad for generating Skolem functions and constants.
+-- ------------------------------------------------------------------------- 
+
+-- | Harrison's code generated skolem functions by adding a prefix to
+-- the variable name they are based on.  Here we have a more general
+-- and type safe solution: we require that variables be instances of
+-- class Skolem which creates Skolem functions based on an integer.
+-- This state value exists in the SkolemT monad during skolemization
+-- and tracks the next available number and the current list of
+-- universally quantified variables.
+
+data SkolemState v term
+    = SkolemState
+      { skolemCount :: Int
+        -- ^ The next available Skolem number.
+      , univQuant :: [v]
+        -- ^ The variables which are universally quantified in the
+        -- current scope, in the order they were encountered.  During
+        -- Skolemization these are the parameters passed to the Skolem
+        -- function.
+      }
+
+-- | The state associated with the Skolem monad.
+newSkolemState :: SkolemState v term
+newSkolemState
+    = SkolemState
+      { skolemCount = 1
+      , univQuant = []
+      }
+
+-- | The Skolem monad transformer
+type SkolemT v term m = StateT (SkolemState v term) m
+
+-- | Run a computation in the Skolem monad.
+runSkolem :: SkolemT v term Identity a -> a
+runSkolem = runIdentity . runSkolemT
+
+-- | The Skolem monad
+type Skolem v term = StateT (SkolemState v term) Identity
+
+-- | Run a computation in a stacked invocation of the Skolem monad.
+runSkolemT :: Monad m => SkolemT v term m a -> m a
+runSkolemT action = (runStateT action) newSkolemState >>= return . fst
+
+-- ------------------------------------------------------------------------- 
+-- Core Skolemization function.                                              
+-- ------------------------------------------------------------------------- 
+
+-- |Skolemize the formula by removing the existential quantifiers and
+-- replacing the variables they quantify with skolem functions (and
+-- constants, which are functions of zero variables.)  The Skolem
+-- functions are new functions (obtained from the SkolemT monad) which
+-- are applied to the list of variables which are universally
+-- quantified in the context where the existential quantifier
+-- appeared.
+skolem :: (Monad m,
+           FirstOrderFormula fof atom v,
+           -- PropositionalFormula pf atom,
+           -- Formula formula term v,
+           Atom atom term v,
+           Term term v f) =>
+          fof -> SkolemT v term m fof
+skolem fm =
+    foldFirstOrder qu co (return . fromBool) (return . atomic) fm
+    where
+      -- We encountered an existentially quantified variable y,
+      -- allocate a new skolem function fx and do a substitution to
+      -- replace occurrences of y with fx.  The value of the Skolem
+      -- function is assumed to equal the value of y which satisfies
+      -- the formula.
+      qu Exists y p =
+          do let xs = fv fm
+             let fx = fApp (C.toSkolem y) (map vt (Set.toAscList xs))
+             skolem (subst (Map.singleton y fx) p)
+      qu Forall x p = skolem p >>= return . for_all x
+      co (BinOp l (:&:) r) = skolem2 (.&.) l r
+      co (BinOp l (:|:) r) = skolem2 (.|.) l r
+      co _ = return fm
+
+skolem2 :: (Monad m,
+            FirstOrderFormula fof atom v,
+            -- PropositionalFormula pf atom,
+            -- Formula formula term v,
+            Atom atom term v,
+            Term term v f) =>
+           (fof -> fof -> fof) -> fof -> fof -> SkolemT v term m fof
+skolem2 cons p q =
+    skolem p >>= \ p' ->
+    skolem q >>= \ q' ->
+    return (cons p' q')
+
+-- ------------------------------------------------------------------------- 
+-- Overall Skolemization function.                                           
+-- ------------------------------------------------------------------------- 
+
+-- |I need to consult the Harrison book for the reasons why we don't
+-- |just Skolemize the result of prenexNormalForm.
+askolemize :: forall m fof atom term v f.
+              (Monad m,
+               FirstOrderFormula fof atom v,
+               Atom atom term v,
+               Term term v f) =>
+              fof -> SkolemT v term m fof
+askolemize = skolem . nnf . simplify
+
+-- | Remove the leading universal quantifiers.  After a call to pnf
+-- this will be all the universal quantifiers, and the skolemization
+-- will have already turned all the existential quantifiers into
+-- skolem functions.
+specialize :: forall fof atom v. FirstOrderFormula fof atom v => fof -> fof
+specialize f =
+    foldFirstOrder q (\ _ -> f) (\ _ -> f) (\ _ -> f) f
+    where
+      q Forall _ f' = specialize f'
+      q _ _ _ = f
+
+-- | Skolemize and then specialize.  Because we know all quantifiers
+-- are gone we can convert to any instance of PropositionalFormula.
+skolemize :: forall m fof atom term v f pf atom2. (Monad m,
+              FirstOrderFormula fof atom v,
+              PropositionalFormula pf atom2,
+              Atom atom term v,
+              Term term v f,
+              Eq pf) =>
+             (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemize ca fm = askolemize fm >>= return . (toPropositional ca :: fof -> pf) . specialize . pnf
+
+{-
+-- | Convert a first order formula into a disjunct of conjuncts of
+-- literals.  Note that this can convert any instance of
+-- FirstOrderFormula into any instance of Literal.
+literal :: forall fof atom term v p f lit. (Literal fof atom, Apply atom p term, Term term v f, Literal lit atom, Formula lit atom, Ord lit) =>
+           fof -> Set.Set (Set.Set lit)
+literal fm =
+    foldLiteral neg tf at fm
+    where
+      neg :: fof -> Set.Set (Set.Set lit)
+      neg x = Set.map (Set.map (.~.)) (literal x)
+      tf = Set.singleton . Set.singleton . fromBool
+      at :: atom -> Set.Set (Set.Set lit)
+      at x = foldApply (\ _ _ -> Set.singleton (Set.singleton (atomic x))) tf x
+-}
+
+-- |We get Skolem Normal Form by skolemizing and then converting to
+-- Prenex Normal Form, and finally eliminating the remaining quantifiers.
+skolemNormalForm :: (FirstOrderFormula fof atom v,
+                     PropositionalFormula pf atom2,
+                     -- Formula fof term v,
+                     -- Formula pf term v,
+                     Atom atom term v,
+                     Term term v f,
+                     Monad m, Ord fof, Eq pf) =>
+                    (atom -> atom2) -> fof -> SkolemT v term m pf
+skolemNormalForm = skolemize
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Tableaux.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Tableaux.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Tableaux.hs
@@ -0,0 +1,623 @@
+{-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Harrison.Tableaux
+    ( unify_literals
+    , unifyAtomsEq
+    , deepen
+    ) where
+
+import Control.Applicative.Error (Failing(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(freeVariables, substitute, unify))
+import Data.Logic.Classes.Equals (AtomEq, zipAtomsEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal, zipLiterals)
+import Data.Logic.Classes.Negate ((.~.), positive)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term(..), vt)
+import Data.Logic.Harrison.FOL (generalize)
+import Data.Logic.Harrison.Lib (allpairs, distrib', settryfind)
+import Data.Logic.Harrison.Prop (simpdnf)
+import Data.Logic.Harrison.Skolem (runSkolem, skolemize)
+import Data.Logic.Harrison.Unif (unify)
+import qualified Data.Map as Map (empty, fromList, Map)
+import qualified Data.Set as Set (empty, map, minView, partition, Set, singleton, toList, union)
+import Data.String (IsString(..))
+import Debug.Trace (trace)
+
+-- =========================================================================
+-- Tableaux, seen as an optimized version of a Prawitz-like procedure.       
+--                                                                           
+-- Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  
+-- ========================================================================= 
+
+-- ------------------------------------------------------------------------- 
+-- Unify literals (just pretend the toplevel relation is a function).        
+-- ------------------------------------------------------------------------- 
+
+unify_literals :: forall lit atom term v f.
+                  (Literal lit atom,
+                   C.Atom atom term v,
+                   Term term v f) =>
+                  Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_literals env f1 f2 =
+    maybe err id (zipLiterals co tf at f1 f2)
+    where
+      -- co :: lit -> lit -> Maybe (Failing (Map.Map v term))
+      co p q = Just $ unify_literals env p q
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      at :: atom -> atom -> Maybe (Failing (Map.Map v term))
+      at a1 a2 = Just $ C.unify env a1 a2
+      err = Failure ["Can't unify literals"]
+
+unifyAtomsEq :: forall v f atom p term.
+                (AtomEq atom p term, Term term v f) =>
+                Map.Map v term -> atom -> atom -> Failing (Map.Map v term)
+unifyAtomsEq env a1 a2 =
+    maybe err id (zipAtomsEq ap tf eq a1 a2)
+    where
+      ap p1 ts1 p2 ts2 =
+          if p1 == p2 && length ts1 == length ts2
+          then Just $ unify env (zip ts1 ts2)
+          else Nothing
+      tf p q = if p == q then Just $ unify env [] else Nothing
+      eq pl pr ql qr = Just $ unify env [(pl, ql), (pr, qr)]
+      err = Failure ["Can't unify atoms"]
+
+-- ------------------------------------------------------------------------- 
+-- Unify complementary literals.                                             
+-- ------------------------------------------------------------------------- 
+
+unify_complements :: forall lit atom term v f.
+                     (Literal lit atom,
+                      C.Atom atom term v,
+                      Term term v f) =>
+                     Map.Map v term -> lit -> lit -> Failing (Map.Map v term)
+unify_complements env p q = unify_literals env p ((.~.) q)
+
+-- ------------------------------------------------------------------------- 
+-- Unify and refute a set of disjuncts.                                      
+-- ------------------------------------------------------------------------- 
+
+unify_refute :: (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) => Set.Set (Set.Set lit) -> Map.Map v term -> Failing (Map.Map v term)
+unify_refute djs env =
+    case Set.minView djs of
+      Nothing -> Success env
+      Just (d, odjs) ->
+          settryfind (\ (p, n) -> unify_complements env p n >>= unify_refute odjs) pairs
+          where
+            pairs = allpairs (,) pos neg
+            (pos,neg) = Set.partition positive d
+
+-- ------------------------------------------------------------------------- 
+-- Hence a Prawitz-like procedure (using unification on DNF).                
+-- ------------------------------------------------------------------------- 
+
+prawitz_loop :: forall atom v term f lit. (Literal lit atom, Term term v f, C.Atom atom term v, Ord lit) =>
+                Set.Set (Set.Set lit) -> [v] -> Set.Set (Set.Set lit) -> Int -> (Map.Map v term, Int)
+prawitz_loop djs0 fvs djs n =
+    let l = length fvs in
+    let newvars = map (\ k -> fromString ("_" ++ show (n * l + k))) [1..l] in
+    let inst = Map.fromList (zip fvs (map vt newvars)) in
+    let djs1 = distrib' (Set.map (Set.map (mapAtoms (atomic . substitute' inst))) djs0) djs in
+    case unify_refute djs1 Map.empty of
+      Failure _ -> prawitz_loop djs0 fvs djs1 (n + 1)
+      Success env -> (env, n + 1)
+    where
+      substitute' :: Map.Map v term -> atom -> atom
+      substitute' = C.substitute
+
+-- prawitz :: forall fof atom v. (FirstOrderFormula fof atom v, Ord fof) => fof -> Int
+prawitz :: forall fof atom term v f lit pf.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal lit atom,
+            Term term v f,
+            C.Atom atom term v) =>
+           fof -> Int
+prawitz fm =
+    snd (prawitz_loop dnf (Set.toList fvs) dnf0 0 :: (Map.Map v term, Int))
+    where
+      dnf0 = (Set.singleton Set.empty) :: Set.Set (Set.Set lit)
+      dnf = simpdnf pf :: Set.Set (Set.Set lit)
+      fvs = foldAtoms (\ s (a :: atom) -> Set.union (C.freeVariables a) s) Set.empty pf :: Set.Set v
+      pf = runSkolem (skolemize id ((.~.)(generalize fm))) :: pf
+
+-- ------------------------------------------------------------------------- 
+-- Examples.                                                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+test01 = TestCase $ assertEqual "p20 - prawitz" expected input
+    where input = prawitz fm
+          fm = (for_all "x" (for_all "y" (exists "z" (for_all "w" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"] .=>.
+                                                                   pApp "R" [vt "z"] .&. pApp "U" [vt "w"]))))) .=>.
+               (exists "x" (exists "y" (pApp "P" [vt "x"] .&. pApp "Q" [vt "y"]))) .=>. (exists "z" (pApp "R" [vt "z"]))
+          expected = 1
+-}
+
+-- ------------------------------------------------------------------------- 
+-- Comparison of number of ground instances.                                 
+-- ------------------------------------------------------------------------- 
+
+{-
+compare :: forall fof pf lit atom term v f.
+           (FirstOrderFormula fof atom v,
+            PropositionalFormula pf atom,
+            Literal pf atom,
+            Term term v f,
+            C.Atom atom term v,
+            IsString f) =>
+           (atom -> Set.Set (f, Int)) -> fof -> (Int, Failing Int)
+-}
+-- compare fa fm = (prawitz fm, davisputnam fa fm)
+{-
+START_INTERACTIVE;;
+test02 = TestCase $ assertEqual "p19" expected input
+    where input = compare (exists "x" (forall "y" (for_all "z" ((pApp "P" [vt "y"] .=>. pApp "Q" [vt "z"]) .=>. pApp "P" [vt "x"] .=>. pApp "Q" [vt "x"]))))
+
+let p20 = compare
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p24 = compare
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"])
+   .=>. (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p39 = compare
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p42 = compare
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+{- **** Too slow?
+
+let p43 = compare
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+ ***** -}
+
+let p44 = compare
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y)))
+   .=>. (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p59 = compare
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = compare
+ <<forall x. P(x,f[vt "x"]) .<=>.
+             exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- More standard tableau procedure, effectively doing DNF incrementally.     
+-- ------------------------------------------------------------------------- 
+
+let rec tableau (fms,lits,n) cont (env,k) =
+  if n < 0 then error "no proof at this level" else
+  match fms with
+    [] -> error "tableau: no proof"
+  | And(p,q) : unexp ->
+      tableau (p : q : unexp,lits,n) cont (env,k)
+  | Or(p,q) : unexp ->
+      tableau (p : unexp,lits,n) (tableau (q : unexp,lits,n) cont) (env,k)
+  | Forall(x,p) : unexp ->
+      let y = Vt("_" ++ string_of_int k) in
+      let p' = subst (x |=> y) p in
+      tableau (p' : unexp@[Forall(x,p)],lits,n-1) cont (env,k+1)
+  | fm : unexp ->
+      try tryfind (\ l -> cont(unify_complements env (fm,l),k)) lits
+      with Failure _ -> tableau (unexp,fm : lits,n) cont (env,k);;
+-}
+
+-- | Try f with higher and higher values of n until it succeeds, or
+-- optional maximum depth limit is exceeded.
+deepen :: (Int -> Failing t) -> Int -> Maybe Int -> Failing (t, Int)
+deepen _ n (Just m) | n > m = Failure ["Exceeded maximum depth limit"]
+deepen f n m =
+    -- If no maximum depth limit is given print a trace of the
+    -- levels tried.  The assumption is that we are running
+    -- interactively.
+    let n' = maybe (trace ("Searching with depth limit " ++ show n) n) (const n) m in
+    case f n' of
+      Failure _ -> deepen f (n + 1) m
+      Success x -> Success (x, n)
+
+{-
+let tabrefute fms =
+  deepen (\ n -> tableau (fms,[],n) (\ x -> x) (Map.empty,0); n) 0;;
+
+let tab fm =
+  let sfm = askolemize(Not(generalize fm)) in
+  if sfm = False then 0 else tabrefute [sfm];;
+
+-- ------------------------------------------------------------------------- 
+-- Example.                                                                  
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p38 = tab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Try to split up the initial formula first; often a big improvement.       
+-- ------------------------------------------------------------------------- 
+
+let splittab fm = 
+  map tabrefute (simpdnf(askolemize(Not(generalize fm))));;
+
+-- ------------------------------------------------------------------------- 
+-- Example: the Andrews challenge.                                           
+-- ------------------------------------------------------------------------- 
+
+START_INTERACTIVE;;
+let p34 = splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Another nice example from EWD 1602.                                       
+-- ------------------------------------------------------------------------- 
+
+let ewd1062 = splittab
+ <<(forall x. x <= x) .&.
+   (forall x y z. x <= y .&. y <= z .=>. x <= z) .&.
+   (forall x y. f[vt "x"] <= y .<=>. x <= g[vt "y"])
+   .=>. (forall x y. x <= y .=>. f[vt "x"] <= f[vt "y"]) .&.
+       (forall x y. x <= y .=>. g[vt "x"] <= g[vt "y"])>>;;
+END_INTERACTIVE;;
+
+-- ------------------------------------------------------------------------- 
+-- Do all the equality-free Pelletier problems, and more, as examples.       
+-- ------------------------------------------------------------------------- 
+
+{- **********
+
+let p1 = time splittab
+ <<p .=>. q .<=>. ~q .=>. ~p>>;;
+
+let p2 = time splittab
+ <<~ ~p .<=>. p>>;;
+
+let p3 = time splittab
+ <<~(p .=>. q) .=>. q .=>. p>>;;
+
+let p4 = time splittab
+ <<~p .=>. q .<=>. ~q .=>. p>>;;
+
+let p5 = time splittab
+ <<(p .|. q .=>. p .|. r) .=>. p .|. (q .=>. r)>>;;
+
+let p6 = time splittab
+ <<p .|. ~p>>;;
+
+let p7 = time splittab
+ <<p .|. ~ ~ ~p>>;;
+
+let p8 = time splittab
+ <<((p .=>. q) .=>. p) .=>. p>>;;
+
+let p9 = time splittab
+ <<(p .|. q) .&. (~p .|. q) .&. (p .|. ~q) .=>. ~(~q .|. ~q)>>;;
+
+let p10 = time splittab
+ <<(q .=>. r) .&. (r .=>. p .&. q) .&. (p .=>. q .&. r) .=>. (p .<=>. q)>>;;
+
+let p11 = time splittab
+ <<p .<=>. p>>;;
+
+let p12 = time splittab
+ <<((p .<=>. q) .<=>. r) .<=>. (p .<=>. (q .<=>. r))>>;;
+
+let p13 = time splittab
+ <<p .|. q .&. r .<=>. (p .|. q) .&. (p .|. r)>>;;
+
+let p14 = time splittab
+ <<(p .<=>. q) .<=>. (q .|. ~p) .&. (~q .|. p)>>;;
+
+let p15 = time splittab
+ <<p .=>. q .<=>. ~p .|. q>>;;
+
+let p16 = time splittab
+ <<(p .=>. q) .|. (q .=>. p)>>;;
+
+let p17 = time splittab
+ <<p .&. (q .=>. r) .=>. s .<=>. (~p .|. q .|. s) .&. (~p .|. ~r .|. s)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Pelletier problems: monadic predicate logic.                              
+-- ------------------------------------------------------------------------- 
+
+let p18 = time splittab
+ <<exists y. forall x. P[vt "y"] .=>. P[vt "x"]>>;;
+
+let p19 = time splittab
+ <<exists x. forall y z. (P[vt "y"] .=>. Q[vt "z"]) .=>. P[vt "x"] .=>. Q[vt "x"]>>;;
+
+let p20 = time splittab
+ <<(forall x y. exists z. forall w. P[vt "x"] .&. Q[vt "y"] .=>. R[vt "z"] .&. U[vt "w"])
+   .=>. (exists x y. P[vt "x"] .&. Q[vt "y"]) .=>. (exists z. R[vt "z"])>>;;
+
+let p21 = time splittab
+ <<(exists x. P .=>. Q[vt "x"]) .&. (exists x. Q[vt "x"] .=>. P)
+   .=>. (exists x. P .<=>. Q[vt "x"])>>;;
+
+let p22 = time splittab
+ <<(forall x. P .<=>. Q[vt "x"]) .=>. (P .<=>. (forall x. Q[vt "x"]))>>;;
+
+let p23 = time splittab
+ <<(forall x. P .|. Q[vt "x"]) .<=>. P .|. (forall x. Q[vt "x"])>>;;
+
+let p24 = time splittab
+ <<~(exists x. U[vt "x"] .&. Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. Q[vt "x"] .|. R[vt "x"]) .&.
+   ~(exists x. P[vt "x"] .=>. (exists x. Q[vt "x"])) .&.
+   (forall x. Q[vt "x"] .&. R[vt "x"] .=>. U[vt "x"]) .=>.
+   (exists x. P[vt "x"] .&. R[vt "x"])>>;;
+
+let p25 = time splittab
+ <<(exists x. P[vt "x"]) .&.
+   (forall x. U[vt "x"] .=>. ~G[vt "x"] .&. R[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. G[vt "x"] .&. U[vt "x"]) .&.
+   ((forall x. P[vt "x"] .=>. Q[vt "x"]) .|. (exists x. Q[vt "x"] .&. P[vt "x"]))
+   .=>. (exists x. Q[vt "x"] .&. P[vt "x"])>>;;
+
+let p26 = time splittab
+ <<((exists x. P[vt "x"]) .<=>. (exists x. Q[vt "x"])) .&.
+   (forall x y. P[vt "x"] .&. Q[vt "y"] .=>. (R[vt "x"] .<=>. U[vt "y"]))
+   .=>. ((forall x. P[vt "x"] .=>. R[vt "x"]) .<=>. (forall x. Q[vt "x"] .=>. U[vt "x"]))>>;;
+
+let p27 = time splittab
+ <<(exists x. P[vt "x"] .&. ~Q[vt "x"]) .&.
+   (forall x. P[vt "x"] .=>. R[vt "x"]) .&.
+   (forall x. U[vt "x"] .&. V[vt "x"] .=>. P[vt "x"]) .&.
+   (exists x. R[vt "x"] .&. ~Q[vt "x"])
+   .=>. (forall x. U[vt "x"] .=>. ~R[vt "x"])
+       .=>. (forall x. U[vt "x"] .=>. ~V[vt "x"])>>;;
+
+let p28 = time splittab
+ <<(forall x. P[vt "x"] .=>. (forall x. Q[vt "x"])) .&.
+   ((forall x. Q[vt "x"] .|. R[vt "x"]) .=>. (exists x. Q[vt "x"] .&. R[vt "x"])) .&.
+   ((exists x. R[vt "x"]) .=>. (forall x. L[vt "x"] .=>. M[vt "x"])) .=>.
+   (forall x. P[vt "x"] .&. L[vt "x"] .=>. M[vt "x"])>>;;
+
+let p29 = time splittab
+ <<(exists x. P[vt "x"]) .&. (exists x. G[vt "x"]) .=>.
+   ((forall x. P[vt "x"] .=>. H[vt "x"]) .&. (forall x. G[vt "x"] .=>. J[vt "x"]) .<=>.
+    (forall x y. P[vt "x"] .&. G[vt "y"] .=>. H[vt "x"] .&. J[vt "y"]))>>;;
+
+let p30 = time splittab
+ <<(forall x. P[vt "x"] .|. G[vt "x"] .=>. ~H[vt "x"]) .&.
+   (forall x. (G[vt "x"] .=>. ~U[vt "x"]) .=>. P[vt "x"] .&. H[vt "x"])
+   .=>. (forall x. U[vt "x"])>>;;
+
+let p31 = time splittab
+ <<~(exists x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"])) .&.
+   (exists x. Q[vt "x"] .&. P[vt "x"]) .&.
+   (forall x. ~H[vt "x"] .=>. J[vt "x"])
+   .=>. (exists x. Q[vt "x"] .&. J[vt "x"])>>;;
+
+let p32 = time splittab
+ <<(forall x. P[vt "x"] .&. (G[vt "x"] .|. H[vt "x"]) .=>. Q[vt "x"]) .&.
+   (forall x. Q[vt "x"] .&. H[vt "x"] .=>. J[vt "x"]) .&.
+   (forall x. R[vt "x"] .=>. H[vt "x"])
+   .=>. (forall x. P[vt "x"] .&. R[vt "x"] .=>. J[vt "x"])>>;;
+
+let p33 = time splittab
+ <<(forall x. P[vt "a"] .&. (P[vt "x"] .=>. P[vt "b"]) .=>. P[vt "c"]) .<=>.
+   (forall x. P[vt "a"] .=>. P[vt "x"] .|. P[vt "c"]) .&. (P[vt "a"] .=>. P[vt "b"] .=>. P[vt "c"])>>;;
+
+let p34 = time splittab
+ <<((exists x. forall y. P[vt "x"] .<=>. P[vt "y"]) .<=>.
+    ((exists x. Q[vt "x"]) .<=>. (forall y. Q[vt "y"]))) .<=>.
+   ((exists x. forall y. Q[vt "x"] .<=>. Q[vt "y"]) .<=>.
+    ((exists x. P[vt "x"]) .<=>. (forall y. P[vt "y"])))>>;;
+
+let p35 = time splittab
+ <<exists x y. P(x,y) .=>. (forall x y. P(x,y))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Full predicate logic (without identity and functions).                    
+-- ------------------------------------------------------------------------- 
+
+let p36 = time splittab
+ <<(forall x. exists y. P(x,y)) .&.
+   (forall x. exists y. G(x,y)) .&.
+   (forall x y. P(x,y) .|. G(x,y)
+   .=>. (forall z. P(y,z) .|. G(y,z) .=>. H(x,z)))
+       .=>. (forall x. exists y. H(x,y))>>;;
+
+let p37 = time splittab
+ <<(forall z.
+     exists w. forall x. exists y. (P(x,z) .=>. P(y,w)) .&. P(y,z) .&.
+     (P(y,w) .=>. (exists u. Q(u,w)))) .&.
+   (forall x z. ~P(x,z) .=>. (exists y. Q(y,z))) .&.
+   ((exists x y. Q(x,y)) .=>. (forall x. R(x,x))) .=>.
+   (forall x. exists y. R(x,y))>>;;
+
+let p38 = time splittab
+ <<(forall x.
+     P[vt "a"] .&. (P[vt "x"] .=>. (exists y. P[vt "y"] .&. R(x,y))) .=>.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .<=>.
+   (forall x.
+     (~P[vt "a"] .|. P[vt "x"] .|. (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))) .&.
+     (~P[vt "a"] .|. ~(exists y. P[vt "y"] .&. R(x,y)) .|.
+     (exists z w. P[vt "z"] .&. R(x,w) .&. R(w,z))))>>;;
+
+let p39 = time splittab
+ <<~(exists x. forall y. P(y,x) .<=>. ~P(y,y))>>;;
+
+let p40 = time splittab
+ <<(exists y. forall x. P(x,y) .<=>. P(x,x))
+  .=>. ~(forall x. exists y. forall z. P(z,y) .<=>. ~P(z,x))>>;;
+
+let p41 = time splittab
+ <<(forall z. exists y. forall x. P(x,y) .<=>. P(x,z) .&. ~P(x,x))
+  .=>. ~(exists z. forall x. P(x,z))>>;;
+
+let p42 = time splittab
+ <<~(exists y. forall x. P(x,y) .<=>. ~(exists z. P(x,z) .&. P(z,x)))>>;;
+
+let p43 = time splittab
+ <<(forall x y. Q(x,y) .<=>. forall z. P(z,x) .<=>. P(z,y))
+   .=>. forall x y. Q(x,y) .<=>. Q(y,x)>>;;
+
+let p44 = time splittab
+ <<(forall x. P[vt "x"] .=>. (exists y. G[vt "y"] .&. H(x,y)) .&.
+   (exists y. G[vt "y"] .&. ~H(x,y))) .&.
+   (exists x. J[vt "x"] .&. (forall y. G[vt "y"] .=>. H(x,y))) .=>.
+   (exists x. J[vt "x"] .&. ~P[vt "x"])>>;;
+
+let p45 = time splittab
+ <<(forall x.
+     P[vt "x"] .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y)) .=>.
+       (forall y. G[vt "y"] .&. H(x,y) .=>. R[vt "y"])) .&.
+   ~(exists y. L[vt "y"] .&. R[vt "y"]) .&.
+   (exists x. P[vt "x"] .&. (forall y. H(x,y) .=>.
+     L[vt "y"]) .&. (forall y. G[vt "y"] .&. H(x,y) .=>. J(x,y))) .=>.
+   (exists x. P[vt "x"] .&. ~(exists y. G[vt "y"] .&. H(x,y)))>>;;
+
+let p46 = time splittab
+ <<(forall x. P[vt "x"] .&. (forall y. P[vt "y"] .&. H(y,x) .=>. G[vt "y"]) .=>. G[vt "x"]) .&.
+   ((exists x. P[vt "x"] .&. ~G[vt "x"]) .=>.
+    (exists x. P[vt "x"] .&. ~G[vt "x"] .&.
+               (forall y. P[vt "y"] .&. ~G[vt "y"] .=>. J(x,y)))) .&.
+   (forall x y. P[vt "x"] .&. P[vt "y"] .&. H(x,y) .=>. ~J(y,x)) .=>.
+   (forall x. P[vt "x"] .=>. G[vt "x"])>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Well-known "Agatha" example; cf. Manthey and Bry, CADE-9.                 
+-- ------------------------------------------------------------------------- 
+
+let p55 = time splittab
+ <<lives(agatha) .&. lives(butler) .&. lives(charles) .&.
+   (killed(agatha,agatha) .|. killed(butler,agatha) .|.
+    killed(charles,agatha)) .&.
+   (forall x y. killed(x,y) .=>. hates(x,y) .&. ~richer(x,y)) .&.
+   (forall x. hates(agatha,x) .=>. ~hates(charles,x)) .&.
+   (hates(agatha,agatha) .&. hates(agatha,charles)) .&.
+   (forall x. lives[vt "x"] .&. ~richer(x,agatha) .=>. hates(butler,x)) .&.
+   (forall x. hates(agatha,x) .=>. hates(butler,x)) .&.
+   (forall x. ~hates(x,agatha) .|. ~hates(x,butler) .|. ~hates(x,charles))
+   .=>. killed(agatha,agatha) .&.
+       ~killed(butler,agatha) .&.
+       ~killed(charles,agatha)>>;;
+
+let p57 = time splittab
+ <<P(f([vt "a"],b),f(b,c)) .&.
+   P(f(b,c),f(a,c)) .&.
+   (forall [vt "x"] y z. P(x,y) .&. P(y,z) .=>. P(x,z))
+   .=>. P(f(a,b),f(a,c))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- See info-hol, circa 1500.                                                 
+-- ------------------------------------------------------------------------- 
+
+let p58 = time splittab
+ <<forall P Q R. forall x. exists v. exists w. forall y. forall z.
+    ((P[vt "x"] .&. Q[vt "y"]) .=>. ((P[vt "v"] .|. R[vt "w"])  .&. (R[vt "z"] .=>. Q[vt "v"])))>>;;
+
+let p59 = time splittab
+ <<(forall x. P[vt "x"] .<=>. ~P(f[vt "x"])) .=>. (exists x. P[vt "x"] .&. ~P(f[vt "x"]))>>;;
+
+let p60 = time splittab
+ <<forall x. P(x,f[vt "x"]) .<=>.
+            exists y. (forall z. P(z,y) .=>. P(z,f[vt "x"])) .&. P(x,y)>>;;
+
+-- ------------------------------------------------------------------------- 
+-- From Gilmore's classic paper.                                             
+-- ------------------------------------------------------------------------- 
+
+{- **** This is still too hard for us! Amazing...
+
+let gilmore_1 = time splittab
+ <<exists x. forall y z.
+      ((F[vt "y"] .=>. G[vt "y"]) .<=>. F[vt "x"]) .&.
+      ((F[vt "y"] .=>. H[vt "y"]) .<=>. G[vt "x"]) .&.
+      (((F[vt "y"] .=>. G[vt "y"]) .=>. H[vt "y"]) .<=>. H[vt "x"])
+      .=>. F[vt "z"] .&. G[vt "z"] .&. H[vt "z"]>>;;
+
+ ***** -}
+
+{- ** This is not valid, according to Gilmore
+
+let gilmore_2 = time splittab
+ <<exists x y. forall z.
+        (F(x,z) .<=>. F(z,y)) .&. (F(z,y) .<=>. F(z,z)) .&. (F(x,y) .<=>. F(y,x))
+        .=>. (F(x,y) .<=>. F(x,z))>>;;
+
+ ** -}
+
+let gilmore_3 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. H[vt "x"])) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. H[vt "z"]) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_4 = time splittab
+ <<exists x y. forall z.
+        (F(x,y) .=>. F(y,z) .&. F(z,z)) .&.
+        (F(x,y) .&. G(x,y) .=>. G(x,z) .&. G(z,z))>>;;
+
+let gilmore_5 = time splittab
+ <<(forall x. exists y. F(x,y) .|. F(y,x)) .&.
+   (forall x y. F(y,x) .=>. F(y,y))
+   .=>. exists z. F(z,z)>>;;
+
+let gilmore_6 = time splittab
+ <<forall x. exists y.
+        (exists u. forall v. F(u,x) .=>. G(v,u) .&. G(u,x))
+        .=>. (exists u. forall v. F(u,y) .=>. G(v,u) .&. G(u,y)) .|.
+            (forall u v. exists w. G(v,u) .|. H(w,y,u) .=>. G(u,w))>>;;
+
+let gilmore_7 = time splittab
+ <<(forall x. K[vt "x"] .=>. exists y. L[vt "y"] .&. (F(x,y) .=>. G(x,y))) .&.
+   (exists z. K[vt "z"] .&. forall u. L[vt "u"] .=>. F(z,u))
+   .=>. exists v w. K[vt "v"] .&. L[vt "w"] .&. G(v,w)>>;;
+
+let gilmore_8 = time splittab
+ <<exists x. forall y z.
+        ((F(y,z) .=>. (G[vt "y"] .=>. (forall u. exists v. H(u,v,x)))) .=>. F(x,x)) .&.
+        ((F(z,x) .=>. G[vt "x"]) .=>. (forall u. exists v. H(u,v,z))) .&.
+        F(x,y)
+        .=>. F(z,z)>>;;
+
+let gilmore_9 = time splittab
+ <<forall x. exists y. forall z.
+        ((forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+          .=>. (forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))) .&.
+        ((forall u. exists v. F(x,u,v) .&. G(y,u) .&. ~H(x,y))
+         .=>. ~(forall u. exists v. F(x,u,v) .&. G(z,u) .&. ~H(x,z))
+         .=>. (forall u. exists v. F(y,u,v) .&. G(y,u) .&. ~H(y,x)) .&.
+             (forall u. exists v. F(z,u,v) .&. G(y,u) .&. ~H(z,y)))>>;;
+
+-- ------------------------------------------------------------------------- 
+-- Example from Davis-Putnam papers where Gilmore procedure is poor.         
+-- ------------------------------------------------------------------------- 
+
+let davis_putnam_example = time splittab
+ <<exists x. exists y. forall z.
+        (F(x,y) .=>. (F(y,z) .&. F(z,z))) .&.
+        ((F(x,y) .&. G(x,y)) .=>. (G(x,z) .&. G(z,z)))>>;;
+
+************ -}
+
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Harrison/Unif.hs b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Unif.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Harrison/Unif.hs
@@ -0,0 +1,125 @@
+{-# OPTIONS -Wall #-}
+module Data.Logic.Harrison.Unif
+    ( unify
+    , solve
+    , fullUnify
+    , unifyAndApply
+    ) where
+
+import Data.Logic.Classes.Term (Term(..), tsubst)
+import Data.Logic.Failing (Failing(..), failing)
+import qualified Data.Map as Map
+{-
+(* ========================================================================= *)
+(* Unification for first order terms.                                        *)
+(*                                                                           *)
+(* Copyright (c) 2003-2007, John Harrison. (See "LICENSE.txt" for details.)  *)
+(* ========================================================================= *)
+
+let rec istriv env x t =
+  match t with
+    Var y -> y = x or defined env y & istriv env x (apply env y)
+  | Fn(f,args) -> exists (istriv env x) args & failwith "cyclic";;
+-}
+isTrivial :: Term term v f => Map.Map v term -> v -> term -> Failing Bool
+isTrivial env x t =
+    foldTerm v f t
+    where
+      v y =
+          if x == y
+          then Success True
+          else maybe (Success False) (isTrivial env x) (Map.lookup y env)
+      f _ args =
+          if any (failing (const False) id . isTrivial env x) args
+          then Failure ["cyclic"]
+          else Success False
+
+{-
+    foldT (\ y -> y == x || (defined env y && istriv env x (apply env y)))
+          (\ _ args -> if any (istriv env x) args then error "cyclic" else False)
+          t
+-}
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Main unification procedure                                                *)
+(* ------------------------------------------------------------------------- *)
+
+let rec unify env eqs =
+  match eqs with
+    [] -> env
+  | (Fn(f,fargs),Fn(g,gargs))::oth ->
+        if f = g & length fargs = length gargs
+        then unify env (zip fargs gargs @ oth)
+        else failwith "impossible unification"
+  | (Var x,t)::oth | (t,Var x)::oth ->
+        if defined env x then unify env ((apply env x,t)::oth)
+        else unify (if istriv env x t then env else (x|->t) env) oth;;
+-}
+unify :: Term term v f => Map.Map v term -> [(term,term)] -> Failing (Map.Map v term)
+unify env [] = Success env
+unify env ((a,b):oth) =
+    foldTerm (vr b) (\ f fargs -> foldTerm (vr a) (fn f fargs) b) a
+    where
+      vr t x =
+          maybe (isTrivial env x t >>= \ trivial -> unify (if trivial then env else Map.insert x t env) oth)
+                (\ y -> unify env ((y, t) : oth))
+                (Map.lookup x env)
+      fn f fargs g gargs =
+          if f == g && length fargs == length gargs
+          then unify env (zip fargs gargs ++ oth)
+          else Failure ["impossible unification"]
+
+{-
+(* ------------------------------------------------------------------------- *)
+(* Solve to obtain a single instantiation.                                   *)
+(* ------------------------------------------------------------------------- *)
+
+let rec solve env =
+  let env' = mapf (tsubst env) env in
+  if env' = env then env else solve env';;
+-}
+solve :: Term term v f => Map.Map v term -> Map.Map v term
+solve env =
+    if env' == env then env else solve env'
+    where env' = Map.map (tsubst env) env
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Unification reaching a final solved form (often this isn't needed).       *)
+(* ------------------------------------------------------------------------- *)
+
+let fullunify eqs = solve (unify undefined eqs);;
+-}
+fullUnify :: Term term v f => [(term,term)] -> Failing (Map.Map v term)
+fullUnify eqs = failing Failure (Success . solve) (unify Map.empty eqs)
+{-
+
+(* ------------------------------------------------------------------------- *)
+(* Examples.                                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+let unify_and_apply eqs =
+  let i = fullunify eqs in
+  let apply (t1,t2) = tsubst i t1,tsubst i t2 in
+  map apply eqs;;
+-}
+unifyAndApply :: Term term v f => [(term, term)] -> Failing [(term, term)]
+unifyAndApply eqs =
+    case fullUnify eqs of
+      Failure x -> Failure x
+      Success i -> Success (map (\ (t1, t2) -> (tsubst i t1, tsubst i t2)) eqs)
+{-
+
+START_INTERACTIVE;;
+unify_and_apply [<<|f(x,g(y))|>>,<<|f(f(z),w)|>>];;
+
+unify_and_apply [<<|f(x,y)|>>,<<|f(y,x)|>>];;
+
+(****  unify_and_apply [<<|f(x,g(y))|>>,<<|f(y,x)|>>];; *****)
+
+unify_and_apply [<<|x_0|>>,<<|f(x_1,x_1)|>>;
+                 <<|x_1|>>,<<|f(x_2,x_2)|>>;
+                 <<|x_2|>>,<<|f(x_3,x_3)|>>];;
+END_INTERACTIVE;;
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Instances/Chiou.hs b/testdata/split-merge-merge-expected/Data/Logic/Instances/Chiou.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Instances/Chiou.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             RankNTypes, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall -Wwarn -fno-warn-orphans -fno-warn-missing-signatures #-}
+module Data.Logic.Instances.Chiou
+    ( Sentence(..)
+    , CTerm(..)
+    , Connective(..)
+    , Quantifier(..)
+    , ConjunctiveNormalForm(..)
+    , NormalSentence(..)
+    , NormalTerm(..)
+    , toSentence
+    , fromSentence
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..), false, true)
+import Data.Logic.Classes.Equals ((.=.), AtomEq(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, pApp, prettyFirstOrder, Quant(..), quant')
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Negate ((.~.), Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Function, Term(..))
+import Data.Logic.Classes.Variable (Variable)
+import Data.String (IsString(..))
+
+data Sentence v p f
+    = Connective (Sentence v p f) Connective (Sentence v p f)
+    | Quantifier Quantifier [v] (Sentence v p f)
+    | Not (Sentence v p f)
+    | Predicate p [CTerm v f]
+    | Equal (CTerm v f) (CTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+data CTerm v f
+    = Function f [CTerm v f]
+    | Variable v
+    deriving (Eq, Ord, Data, Typeable)
+
+data Connective
+    = Imply
+    | Equiv
+    | And
+    | Or
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+data Quantifier
+    = ForAll
+    | ExistsCh
+    deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Negatable (Sentence v p f) where
+    negatePrivate = Not
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Constants p, Eq (Sentence v p f)) => Constants (Sentence v p f) where
+    fromBool x = Predicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance ({- Constants (Sentence v p f), -} Ord v, Ord p, Ord f) => Combinable (Sentence v p f) where
+    x .<=>. y = Connective x Equiv y
+    x .=>.  y = Connective x Imply y
+    x .|.   y = Connective x Or y
+    x .&.   y = Connective x And y
+
+instance (Predicate p, Function f v) => Formula (Sentence v p f) (Sentence v p f) where
+    atomic (Connective _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Quantifier _ _ _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic (Not _) = error "Logic.Instances.Chiou.atomic: unexpected"
+    atomic x@(Predicate _ _) = x
+    atomic x@(Equal _ _) = x
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f), Variable v, Predicate p, Function f v, Combinable (Sentence v p f)) =>
+         PropositionalFormula (Sentence v p f) (Sentence v p f) where
+    foldPropositional co tf at formula =
+        case formula of
+          Not x -> co ((:~:) x)
+          Quantifier _ _ _ -> error "Logic.Instance.Chiou.foldF0: unexpected"
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate p ts -> maybe (at (Predicate p ts)) tf (asBool p)
+          Equal t1 t2 -> at (Equal t1 t2)
+
+data AtomicFunction v
+    = AtomicFunction String
+    -- This is redundant with the SkolemFunction and SkolemConstant
+    -- constructors in the Chiou Term type.
+    | AtomicSkolemFunction v
+    deriving (Eq, Show)
+
+instance IsString (AtomicFunction v) where
+    fromString = AtomicFunction
+
+instance Variable v => Skolem (AtomicFunction v) v where
+    toSkolem = AtomicSkolemFunction
+    isSkolem (AtomicSkolemFunction _) = True
+    isSkolem _ = False
+
+-- The Atom type is not cleanly distinguished from the Sentence type, so we need an Atom instance for Sentence.
+instance (Variable v, Predicate p, Function f v) => Apply (Sentence v p f) p (CTerm v f) where
+    foldApply ap tf (Predicate p ts) = maybe (ap p ts) tf (asBool p)
+    foldApply _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    apply' = Predicate
+
+instance Predicate p => AtomEq (Sentence v p f) p (CTerm v f) where
+    foldAtomEq ap tf _ (Predicate p ts) = if p == true then tf True else if p == false then tf False else ap p ts
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    foldAtomEq _ _ _ _ = error "Data.Logic.Instances.Chiou: Invalid atom"
+    equals = Equal
+    applyEq' = Predicate
+
+instance (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Variable v, Predicate p, Function f v) => Pretty (Sentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Formula (Sentence v p f) (Sentence v p f), Predicate p, Function f v, Variable v) => HasFixity (Sentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Formula (Sentence v p f) (Sentence v p f),
+          Variable v, Predicate p, Function f v) =>
+          FirstOrderFormula (Sentence v p f) (Sentence v p f) v where
+    for_all v x = Quantifier ForAll [v] x
+    exists v x = Quantifier ExistsCh [v] x
+    foldFirstOrder qu co tf at f =
+        case f of
+          Not x -> co ((:~:) x)
+          Quantifier op (v:vs) f' ->
+              let op' = case op of
+                          ForAll -> Forall
+                          ExistsCh -> Exists in
+              -- Use Logic.quant' here instead of the constructor
+              -- Quantifier so as not to create quantifications with
+              -- empty variable lists.
+              qu op' v (quant' op' vs f')
+          Quantifier _ [] f' -> foldFirstOrder qu co tf at f'
+          Connective f1 Imply f2 -> co (BinOp f1 (:=>:) f2)
+          Connective f1 Equiv f2 -> co (BinOp f1 (:<=>:) f2)
+          Connective f1 And f2 -> co (BinOp f1 (:&:) f2)
+          Connective f1 Or f2 -> co (BinOp f1 (:|:) f2)
+          Predicate _ _ -> at f
+          Equal _ _ -> at f
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Not f1', Not f2') -> co ((:~:) f1') ((:~:) f2')
+          (Quantifier op1 (v1:vs1) f1', Quantifier op2 (v2:vs2) f2') ->
+              if op1 == op2
+              then let op' = case op1 of
+                               ForAll -> Forall
+                               ExistsCh -> Exists in
+                   qu op' v1 (Quantifier op1 vs1 f1') Forall v2 (Quantifier op2 vs2 f2')
+              else Nothing
+          (Quantifier q1 [] f1', Quantifier q2 [] f2') ->
+              if q1 == q2 then zipFirstOrder qu co tf at f1' f2' else Nothing
+          (Connective l1 op1 r1, Connective l2 op2 r2) ->
+              case (op1, op2) of
+                (And, And) -> co (BinOp l1 (:&:) r1) (BinOp l2 (:&:) r2)
+                (Or, Or) -> co (BinOp l1 (:|:) r1) (BinOp l2 (:|:) r2)
+                (Imply, Imply) -> co (BinOp l1 (:=>:) r1) (BinOp l2 (:=>:) r2)
+                (Equiv, Equiv) -> co (BinOp l1 (:<=>:) r1) (BinOp l2 (:<=>:) r2)
+                _ -> Nothing
+          (Equal _ _, Equal _ _) -> at f1 f2
+          (Predicate _ _, Predicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Variable v, Function f v) => Term (CTerm v f) v f where
+    foldTerm v fn t =
+        case t of
+          Variable x -> v x
+          Function f ts -> fn f ts
+    zipTerms  v f t1 t2 =
+        case (t1, t2) of
+          (Variable v1, Variable v2) -> v v1 v2
+          (Function f1 ts1, Function f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Variable
+    fApp f ts = Function f ts
+
+data ConjunctiveNormalForm v p f =
+    CNF [Sentence v p f]
+    deriving (Eq)
+
+data NormalSentence v p f
+    = NFNot (NormalSentence v p f)
+    | NFPredicate p [NormalTerm v f]
+    | NFEqual (NormalTerm v f) (NormalTerm v f)
+    deriving (Eq, Ord, Data, Typeable)
+
+-- We need a distinct type here because of the functional dependencies
+-- in class FirstOrderFormula.
+data NormalTerm v f
+    = NormalFunction f [NormalTerm v f]
+    | NormalVariable v
+    deriving (Eq, Ord, Data, Typeable)
+
+instance (Constants p, Eq (NormalSentence v p f)) => Constants (NormalSentence v p f) where
+    fromBool x = NFPredicate (fromBool x) []
+    asBool x
+        | fromBool True == x = Just True
+        | fromBool False == x = Just False
+        | True = Nothing
+
+instance Negatable (NormalSentence v p f) where
+    negatePrivate = NFNot
+    foldNegation normal inverted (NFNot x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+{-
+instance (Arity p, Constants p, Combinable (NormalSentence v p f)) => Pred p (NormalTerm v f) (NormalSentence v p f) where
+    pApp0 x = NFPredicate x []
+    pApp1 x a = NFPredicate x [a]
+    pApp2 x a b = NFPredicate x [a,b]
+    pApp3 x a b c = NFPredicate x [a,b,c]
+    pApp4 x a b c d = NFPredicate x [a,b,c,d]
+    pApp5 x a b c d e = NFPredicate x [a,b,c,d,e]
+    pApp6 x a b c d e f = NFPredicate x [a,b,c,d,e,f]
+    pApp7 x a b c d e f g = NFPredicate x [a,b,c,d,e,f,g]
+    x .=. y = NFEqual x y
+    x .!=. y = NFNot (NFEqual x y)
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Variable v, Predicate p, Function f v, Combinable (NormalSentence v p f)) => Pretty (NormalSentence v p f) where
+    pretty = prettyFirstOrder (\ _ a -> pretty a) pretty 0
+
+instance (Predicate p, Function f v, Combinable (NormalSentence v p f)) => Formula (NormalSentence v p f) (NormalSentence v p f) where
+    atomic x@(NFPredicate _ _) = x
+    atomic x@(NFEqual _ _) = x
+    atomic _ = error "Chiou: atomic"
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f), Combinable (NormalSentence v p f), Term (NormalTerm v f) v f,
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (NormalSentence v p f) (NormalSentence v p f) v where
+    for_all _ _ = error "FirstOrderFormula NormalSentence"
+    exists _ _ = error "FirstOrderFormula NormalSentence"
+    foldFirstOrder _ co tf at f =
+        case f of
+          NFNot x -> co ((:~:) x)
+          NFEqual _ _ -> at f
+          NFPredicate p _ -> maybe (at f) tf (asBool p)
+{-
+    zipFirstOrder _ co tf at f1 f2 =
+        case (f1, f2) of
+          (NFNot f1', NFNot f2') -> co ((:~:) f1') ((:~:) f2')
+          (NFEqual _ _, NFEqual _ _) -> at f1 f2
+          (NFPredicate _ _, NFPredicate _ _) -> at f1 f2
+          _ -> Nothing
+-}
+
+instance (Formula (NormalSentence v p f) (NormalSentence v p f),
+          Combinable (NormalSentence v p f), Predicate p, Function f v, Variable v) => HasFixity (NormalSentence v p f) where
+    fixity = fixityFirstOrder
+
+instance (Variable v, Function f v) => Term (NormalTerm v f) v f where
+    vt = NormalVariable
+    fApp = NormalFunction
+    foldTerm v f t =
+            case t of
+              NormalVariable x -> v x
+              NormalFunction x ts -> f x ts
+    zipTerms v fn t1 t2 =
+        case (t1, t2) of
+          (NormalVariable x1, NormalVariable x2) -> v x1 x2
+          (NormalFunction f1 ts1, NormalFunction f2 ts2) -> fn f1 ts1 f2 ts2
+          _ -> Nothing
+
+toSentence :: (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Atom (Sentence v p f) (CTerm v f) v, Function f v, Variable v, Predicate p) =>
+              NormalSentence v p f -> Sentence v p f
+toSentence (NFNot s) = (.~.) (toSentence s)
+toSentence (NFEqual t1 t2) = toTerm t1 .=. toTerm t2
+toSentence (NFPredicate p ts) = pApp p (map toTerm ts)
+
+toTerm :: (Variable v, Function f v) => NormalTerm v f -> CTerm v f
+toTerm (NormalFunction f ts) = fApp f (map toTerm ts)
+toTerm (NormalVariable v) = vt v
+
+fromSentence :: forall v p f. (FirstOrderFormula (Sentence v p f) (Sentence v p f) v, Predicate p) =>
+                Sentence v p f -> NormalSentence v p f
+fromSentence = foldFirstOrder 
+                 (\ _ _ _ -> error "fromSentence 1")
+                 (\ cm ->
+                      case cm of
+                        ((:~:) f) -> NFNot (fromSentence f)
+                        _ -> error "fromSentence 2")
+                 (\ x -> NFPredicate (fromBool x) [])
+                 (foldAtomEq (\ p ts -> NFPredicate p (map fromTerm ts))
+                             (\ x -> NFPredicate (fromBool x) [])
+                             (\ t1 t2 -> NFEqual (fromTerm t1) (fromTerm t2)))
+
+fromTerm :: CTerm v f -> NormalTerm v f
+fromTerm (Function f ts) = NormalFunction f (map fromTerm ts)
+fromTerm (Variable v) = NormalVariable v
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Instances/PropLogic.hs b/testdata/split-merge-merge-expected/Data/Logic/Instances/PropLogic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Instances/PropLogic.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.PropLogic
+    ( flatten
+    , plSat0
+    , plSat
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(fromBool, asBool))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Formula (Formula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (clauseNormalForm', fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set.Extra as S (Set, toList)
+import PropLogic (PropAlg(satisfiable), PropForm(..))
+
+instance Negatable (PropForm a) where
+    negatePrivate = N
+    foldNegation normal inverted (N x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance {- Ord a => -} Combinable (PropForm a) where
+    x .<=>. y = EJ [x, y]
+    x .=>.  y = SJ [x, y]
+    x .|.   y = DJ [x, y]
+    x .&.   y = CJ [x, y]
+
+instance (Pretty a, HasFixity a, Ord a) => Formula (PropForm a) a where
+    atomic = A
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (PropForm a), Pretty a, HasFixity a, Ord a) => PropositionalFormula (PropForm a) a where
+    foldPropositional co tf at formula =
+        case formula of
+          -- EJ [x,y,z,...] -> CJ [EJ [x,y], EJ[y,z], ...]
+          EJ [] -> error "Empty equijunct"
+          EJ [x] -> foldPropositional co tf at x
+          EJ [x0, x1] -> co (BinOp x0 (:<=>:) x1)
+          EJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> EJ [x0, x1]) (pairs xs)))
+          SJ [] -> error "Empty subjunct"
+          SJ [x] -> foldPropositional co tf at x
+          SJ [x0, x1] -> co (BinOp x0 (:=>:) x1)
+          SJ xs -> foldPropositional co tf at (CJ (map (\ (x0, x1) -> SJ [x0, x1]) (pairs xs)))
+          DJ [] -> tf False
+          DJ [x] -> foldPropositional co tf at x
+          DJ (x0:xs) -> co (BinOp x0 (:|:) (DJ xs))
+          CJ [] -> tf True
+          CJ [x] -> foldPropositional co tf at x
+          CJ (x0:xs) -> co (BinOp x0 (:&:) (CJ xs))
+          N x -> co ((:~:) x)
+          -- Not sure what to do about these - so far not an issue.
+          T -> tf True
+          F -> tf False
+          A x -> at x
+
+instance Constants (PropForm formula) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (PropositionalFormula (PropForm atom) atom, Pretty atom, HasFixity atom) => Pretty (PropForm atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (PropositionalFormula (PropForm atom) atom, HasFixity atom) => HasFixity (PropForm atom) where
+    fixity = fixityPropositional
+
+pairs :: [a] -> [(a, a)]
+pairs (x:y:zs) = (x,y) : pairs (y:zs)
+pairs _ = []
+
+flatten :: PropForm a -> PropForm a
+flatten (CJ xs) =
+    CJ (concatMap f (map flatten xs))
+    where
+      f (CJ ys) = ys
+      f x = [x]
+flatten (DJ xs) =
+    DJ (concatMap f (map flatten xs))
+    where
+      f (DJ ys) = ys
+      f x = [x]
+flatten (EJ xs) = EJ (map flatten xs)
+flatten (SJ xs) = SJ (map flatten xs)
+flatten (N x) = N (flatten x)
+flatten x = x
+
+plSat0 :: (PropAlg a (PropForm formula), PropositionalFormula formula atom, Ord formula) => PropForm formula -> Bool
+plSat0 f = satisfiable . (\ (x :: PropForm formula) -> x) . clauses0 $ f
+
+clauses0 :: (PropositionalFormula formula atom, Ord formula) => PropForm formula -> PropForm formula
+clauses0 f = CJ . map DJ . map S.toList . S.toList $ clauseNormalForm' f
+
+plSat :: forall m formula atom term v f. (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+                formula -> SkolemT v term m Bool
+plSat f = clauses f >>= (\ (x :: PropForm formula) -> return x) >>= return . satisfiable
+
+clauses :: forall m formula atom term v f.
+           (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Atom atom term v, Term term v f, Eq formula, Literal formula atom, Ord formula) =>
+           formula -> SkolemT v term m (PropForm formula)
+clauses f =
+    do (cnf :: S.Set (S.Set formula)) <- clauseNormalForm f
+       return . CJ . map DJ . map (map A) . map S.toList . S.toList $ cnf
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Instances/SatSolver.hs b/testdata/split-merge-merge-expected/Data/Logic/Instances/SatSolver.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Instances/SatSolver.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS -fno-warn-orphans #-}
+module Data.Logic.Instances.SatSolver where
+
+import Control.Monad.State (get, put)
+import Control.Monad.Trans (lift)
+import Data.Boolean.SatSolver (assertTrue', CNF, Literal(..), newSatSolver, solve)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.ClauseNormalForm (ClauseNormalFormula(..))
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import qualified Data.Logic.Classes.Literal as N (Literal)
+import Data.Logic.Classes.Negate ((.~.), Negatable(..), negated)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import Data.Logic.Normal.Implicative (LiteralMapT, NormalT)
+import qualified Data.Map as M (insert, lookup)
+import qualified Data.Set.Extra as S (fromList, ssMapM, toList)
+
+instance Ord Literal where
+    compare (Neg _) (Pos _) = LT
+    compare (Pos _) (Neg _) = GT
+    compare (Pos m) (Pos n) = compare m n
+    compare (Neg m) (Neg n) = compare m n
+
+instance Negatable Literal where
+    negatePrivate (Neg x) = Pos x
+    negatePrivate (Pos x) = Neg x
+    foldNegation _ inverted (Neg x) = inverted (Pos x)
+    foldNegation normal _ (Pos x) = normal (Pos x)
+
+deriving instance Data Literal
+deriving instance Typeable Literal
+
+instance ClauseNormalFormula CNF Literal where
+    clauses = S.fromList . map S.fromList
+    makeCNF = map S.toList . S.toList
+    satisfiable cnf = return . not . null $ assertTrue' cnf newSatSolver >>= solve
+
+toCNF :: (Monad m,
+          FirstOrderFormula formula atom v,
+          PropositionalFormula formula atom,
+          Atom atom term v,
+          AtomEq atom p term,
+          Term term v f,
+          N.Literal formula atom,
+          Ord formula) =>
+         formula -> NormalT formula v term m CNF
+toCNF f = clauseNormalForm f >>= S.ssMapM (lift . toLiteral) >>= return . makeCNF
+
+-- |Convert a [[formula]] to CNF, which means building a map from
+-- formula to Literal.
+toLiteral :: forall m lit. (Monad m, Negatable lit, Ord lit) =>
+             lit -> LiteralMapT lit m Literal
+toLiteral f =
+    literalNumber >>= return . if negated f then Neg else Pos
+    where
+      literalNumber :: LiteralMapT lit m Int
+      literalNumber =
+          get >>= \ (count, m) ->
+          case M.lookup f' m of
+            Nothing -> do let m' = M.insert f' count m
+                          put (count+1, m') 
+                          return count
+            Just n -> return n
+      f' :: lit
+      f' = if negated f then (.~.) f else f
diff --git a/testdata/split-merge-merge-expected/Data/Logic/KnowledgeBase.hs b/testdata/split-merge-merge-expected/Data/Logic/KnowledgeBase.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/KnowledgeBase.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, PackageImports,
+             RankNTypes, TemplateHaskell, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS -Wall #-}
+
+{- KnowledgeBase.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.KnowledgeBase
+    ( WithId(WithId, wiItem, wiIdent) -- Probably only used by some unit tests, and not really correctly
+    , ProverT
+    , runProver'
+    , runProverT'
+    , getKB
+    , unloadKB
+    -- , deleteKB
+    , askKB
+    , theoremKB
+    , inconsistantKB
+    , ProofResult(Proved, Disproved, Invalid)
+    , Proof(Proof, proofResult, proof)
+    , validKB
+    , tellKB
+    , loadKB
+    , showKB
+    ) where
+
+import "mtl" Control.Monad.Identity (Identity(runIdentity))
+import "mtl" Control.Monad.State (evalStateT, MonadState(get, put), StateT)
+import "mtl" Control.Monad.Trans (lift)
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm)
+import Data.Logic.Resolution (getSetOfSupport, prove, SetOfSupport)
+import Data.SafeCopy (base, deriveSafeCopy)
+import qualified Data.Set.Extra as S (empty, map, minView, null, partition, Set, union)
+import Prelude hiding (negate)
+
+type SentenceCount = Int
+
+data WithId a = WithId {wiItem :: a, wiIdent :: Int} deriving (Eq, Ord, Show, Data, Typeable)
+
+withId :: Int -> a -> WithId a
+withId i x = WithId {wiIdent = i, wiItem = x}
+
+{-
+withIdPairs :: [WithId a] -> [(a, Int)]
+withIdPairs = map (\ x -> (wiItem x, wiIdent x))
+
+wiLookupId :: Eq a => a -> [WithId a] -> Maybe Int
+wiLookupId x xs = lookup x (withIdPairs xs)
+
+withIdPairs' :: [WithId a] -> [(Int, a)]
+withIdPairs' = map (\ x -> (wiIdent x, wiItem x))
+
+wiLookupItem :: Int -> [WithId a] -> Maybe a
+wiLookupItem i xs = lookup i (withIdPairs' xs)
+-}
+
+type KnowledgeBase inf = S.Set (WithId inf)
+
+data ProverState inf
+    = ProverState
+      { recursionLimit :: Maybe Int
+      , knowledgeBase :: KnowledgeBase inf
+      , sentenceCount :: Int }
+
+zeroKB :: Maybe Int -> ProverState inf
+zeroKB limit =
+    ProverState
+         { recursionLimit = limit
+         , knowledgeBase = S.empty
+         , sentenceCount = 1 }
+
+-- |A monad for running the knowledge base.
+type ProverT inf = StateT (ProverState inf)
+type ProverT' v term inf m a = ProverT inf (SkolemT v term m) a
+
+runProverT' :: Monad m => Maybe Int -> ProverT' v term inf m a -> m a
+runProverT' limit = runSkolemT . runProverT limit
+runProverT :: Monad m => Maybe Int -> StateT (ProverState inf) m a -> m a
+runProverT limit action = evalStateT action (zeroKB limit)
+runProver' :: Maybe Int -> ProverT' v term inf Identity a -> a
+runProver' limit = runIdentity . runProverT' limit
+{-
+runProver :: StateT (ProverState inf) Identity a -> a
+runProver = runIdentity . runProverT
+-}
+
+data ProofResult
+    = Disproved
+    -- ^ The conjecture is unsatisfiable
+    | Proved
+    -- ^ The negated conjecture is unsatisfiable
+    | Invalid
+    -- ^ Both are satisfiable
+    deriving (Data, Typeable, Eq, Ord, Show)
+
+$(deriveSafeCopy 1 'base ''ProofResult)
+
+data Proof lit = Proof {proofResult :: ProofResult, proof :: S.Set (ImplicativeForm lit)} deriving (Data, Typeable, Eq, Ord)
+
+instance (Ord lit, Show lit, Literal lit atom, FirstOrderFormula lit atom v) => Show (Proof lit) where
+    show p = "Proof {proofResult = " ++ show (proofResult p) ++ ", proof = " ++ show (proof p) ++ "}"
+
+-- |Remove a particular sentence from the knowledge base
+unloadKB :: (Monad m, Ord inf) => SentenceCount -> ProverT inf m (Maybe (KnowledgeBase inf))
+unloadKB n =
+    do st <- get
+       let (discard, keep) = S.partition ((== n) . wiIdent) (knowledgeBase st)
+       put (st {knowledgeBase = keep}) >> return (Just discard)
+
+-- |Return the contents of the knowledgebase.
+getKB :: Monad m => ProverT inf m (S.Set (WithId inf))
+getKB = get >>= return . knowledgeBase
+
+-- |Return a flag indicating whether sentence was disproved, along
+-- with a disproof.
+inconsistantKB :: forall m formula atom term v p f lit.
+                  (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+                   Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+                  formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+inconsistantKB s =
+    get >>= \ st ->
+    lift (implicativeNormalForm s) >>=
+    return . getSetOfSupport >>= \ sos ->
+    getKB >>=
+    return . prove (recursionLimit st) S.empty sos . S.map wiItem
+
+-- |Return a flag indicating whether sentence was proved, along with a
+-- proof.
+theoremKB :: forall m formula atom term v p f lit.
+             (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+              Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+             formula -> ProverT' v term (ImplicativeForm lit) m (Bool, SetOfSupport lit v term)
+theoremKB s = inconsistantKB ((.~.) s)
+
+-- |Try to prove a sentence, return the result and the proof.
+-- askKB should be in KnowledgeBase module. However, since resolution
+-- is here functions are here, it is also placed in this module.
+askKB :: (Monad m, FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+          Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+         formula -> ProverT' v term (ImplicativeForm lit) m Bool
+askKB s = theoremKB s >>= return . fst
+
+-- |See whether the sentence is true, false or invalid.  Return proofs
+-- for truth and falsity.
+validKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+            Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+           formula -> ProverT' v term (ImplicativeForm lit) m (ProofResult, SetOfSupport lit v term, SetOfSupport lit v term)
+validKB s =
+    theoremKB s >>= \ (proved, proof1) ->
+    inconsistantKB s >>= \ (disproved, proof2) ->
+    return (if proved then Proved else if disproved then Disproved else Invalid, proof1, proof2)
+
+-- |Validate a sentence and insert it into the knowledgebase.  Returns
+-- the INF sentences derived from the new sentence, or Nothing if the
+-- new sentence is inconsistant with the current knowledgebase.
+tellKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Data formula, Data lit, Eq lit, Ord lit, Ord term) =>
+          formula -> ProverT' v term (ImplicativeForm lit) m (Proof lit)
+tellKB s =
+    do st <- get
+       inf <- lift (implicativeNormalForm s)
+       let inf' = S.map (withId (sentenceCount st)) inf
+       (valid, _, _) <- validKB s
+       case valid of
+         Disproved -> return ()
+         _ -> put st { knowledgeBase = S.union (knowledgeBase st) inf'
+                     , sentenceCount = sentenceCount st + 1 }
+       return $ Proof {proofResult = valid, proof = S.map wiItem inf'}
+
+loadKB :: (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal lit atom, Atom atom term v, AtomEq atom p term, Term term v f,
+           Monad m, Ord formula, Ord term, Ord lit, Data formula, Data lit) =>
+          [formula] -> ProverT' v term (ImplicativeForm lit) m [Proof lit]
+loadKB sentences = mapM tellKB sentences
+
+-- |Delete an entry from the KB.
+{-
+deleteKB :: Monad m => Int -> ProverT inf m String
+deleteKB i = do st <- get
+                modify (\ st' -> st' {knowledgeBase = deleteElement i (knowledgeBase st')})
+                st' <- get
+		return (if length (knowledgeBase st') /= length (knowledgeBase st) then
+			  "Deleted"
+			else
+			  "Failed to delete")
+	     
+deleteElement :: Int -> [a] -> [a]
+deleteElement i l
+    | i <= 0    = l
+    | otherwise = let
+		    (p1, p2) = splitAt (i - 1) l
+		  in
+		    p1 ++ (case p2 of
+			       [] -> []
+			       _ -> tail p2)
+-}
+
+-- |Return a text description of the contents of the knowledgebase.
+showKB :: (Show inf, Monad m) => ProverT inf m String
+showKB = get >>= return . reportKB
+
+reportKB :: (Show inf) => ProverState inf -> String
+reportKB st@(ProverState {knowledgeBase = kb}) =
+    case S.minView kb of
+      Nothing -> "Nothing in Knowledge Base\n"
+      Just (WithId {wiItem = x, wiIdent = n}, kb')
+          | S.null kb' ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n"
+          | True ->
+              show n ++ ") " ++ "\t" ++ show x ++ "\n" ++ reportKB (st {knowledgeBase = kb'})
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Normal/Clause.hs b/testdata/split-merge-merge-expected/Data/Logic/Normal/Clause.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Normal/Clause.hs
@@ -0,0 +1,93 @@
+-- |A series of transformations to convert first order logic formulas
+-- into (ultimately) Clause Normal Form.
+-- 
+-- @
+-- 1st order formula:
+--   ∀Y (∀X (taller(Y,X) | wise(X)) => wise(Y))
+-- 
+-- Simplify
+--   ∀Y (~∀X (taller(Y,X) | wise(X)) | wise(Y))
+-- 
+-- Move negations in - Negation Normal Form
+--   ∀Y (∃X (~taller(Y,X) & ~wise(X)) | wise(Y))
+-- 
+-- Move quantifiers out - Prenex Normal Form
+--   ∀Y (∃X ((~taller(Y,X) & ~wise(X)) | wise(Y)))
+-- 
+-- Distribute disjunctions
+--   ∀Y ∃X ((~taller(Y,X) | wise(Y)) & (~wise(X) | wise(Y)))
+-- 
+-- Skolemize  - Skolem Normal Form
+--   ∀Y (~taller(Y,x(Y)) | wise(Y)) & (~wise(x(Y)) | wise(Y))
+-- 
+-- Convert to CNF
+--   { ~taller(Y,x(Y)) | wise(Y),
+--     ~wise(x(Y)) | wise(Y) } 
+-- @
+-- 
+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Clause
+    ( clauseNormalForm
+    , cnfTrace
+    ) where
+
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Equals (AtomEq, prettyAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), prettyFirstOrder)
+import Data.Logic.Classes.Literal (Literal(..), prettyLit)
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Normal (simpcnf')
+import Data.Logic.Harrison.Skolem (nnf, pnf, simplify, skolemize, SkolemT)
+import qualified Data.Set.Extra as Set (Set, toList)
+import Text.PrettyPrint (($$), brackets, Doc, hcat, nest, render, text, vcat)
+
+-- |Convert to Skolem Normal Form and then distribute the disjunctions over the conjunctions:
+-- 
+-- @
+-- Formula      Rewrites to
+-- P | (Q & R)  (P | Q) & (P | R)
+-- (Q & R) | P  (Q | P) & (R | P)
+-- @
+-- 
+clauseNormalForm :: forall formula atom term v f lit m.
+                    (Monad m,
+                     FirstOrderFormula formula atom v,
+                     PropositionalFormula formula atom,
+                     Atom atom term v,
+                     Term term v f,
+                     Literal lit atom,
+                     Ord formula, Ord lit) =>
+                    formula -> SkolemT v term m (Set.Set (Set.Set lit))
+clauseNormalForm fm = skolemize id fm >>= return . (simpcnf' :: formula -> Set.Set (Set.Set lit))
+
+cnfTrace :: forall m formula atom term v p f lit.
+            (Monad m,
+             FirstOrderFormula formula atom v,
+             PropositionalFormula formula atom,
+             Atom atom term v,
+             AtomEq atom p term,
+             Term term v f,
+             Literal lit atom,
+             Ord formula, Ord lit) =>
+            (v -> Doc)
+         -> (p -> Doc)
+         -> (f -> Doc)
+         -> formula
+         -> SkolemT v term m (String, Set.Set (Set.Set lit))
+cnfTrace pv pp pf f =
+    do (snf :: formula) <- skolemize id f
+       cnf <- clauseNormalForm f
+       return (render (vcat
+                       [text "Original:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 f),
+                        text "Simplified:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (simplify f)),
+                        text "Negation Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (nnf . simplify $ f)),
+                        text "Prenex Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 (pnf f)),
+                        text "Skolem Normal Form:" $$ nest 2 (prettyFirstOrder (prettyAtomEq pv pp pf) pv 0 snf),
+                        text "Clause Normal Form:" $$ vcat (map prettyClause (fromSS cnf))]), cnf)
+    where
+      prettyClause (clause :: [lit]) =
+          nest 2 . brackets . hcat . intersperse (text ", ") . map (nest 2 . brackets . prettyLit (prettyAtomEq pv pp pf) pv 0) $ clause
+      fromSS = (map Set.toList) . Set.toList 
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Normal/Implicative.hs b/testdata/split-merge-merge-expected/Data/Logic/Normal/Implicative.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Normal/Implicative.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE DeriveDataTypeable, PackageImports, RankNTypes, ScopedTypeVariables #-}
+{-# OPTIONS -Wall #-}
+module Data.Logic.Normal.Implicative
+    ( LiteralMapT
+    , NormalT
+    , runNormal
+    , runNormalT
+    , ImplicativeForm(INF, neg, pos)
+    , makeINF'
+    , implicativeNormalForm
+    , prettyINF
+    , prettyProof
+    ) where
+
+import Control.Monad.Identity (Identity(runIdentity))
+import Control.Monad.State (MonadPlus, msum, StateT(runStateT))
+import Data.Generics (Data, listify, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.Constants (ifElse, true)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Skolem (Skolem(isSkolem))
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (runSkolemT, SkolemT)
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Map as Map (empty, Map)
+import qualified Data.Set.Extra as Set (empty, flatten, fold, fromList, insert, map, Set, singleton, toList)
+import Text.PrettyPrint (cat, Doc, hsep, text)
+
+-- |Combination of Normal monad and LiteralMap monad
+type NormalT formula v term m a = SkolemT v term (LiteralMapT formula m) a
+
+runNormalT :: Monad m => NormalT formula v term m a -> m a
+runNormalT action = runLiteralMapM (runSkolemT action)
+
+runNormal :: NormalT formula v term Identity a -> a
+runNormal = runIdentity . runNormalT
+ 
+--type LiteralMap f = LiteralMapT f Identity
+type LiteralMapT f = StateT (Int, Map.Map f Int)
+
+--runLiteralMap :: LiteralMap p a -> a
+--runLiteralMap action = runIdentity (runLiteralMapM action)
+
+runLiteralMapM :: Monad m => LiteralMapT f m a -> m a
+runLiteralMapM action = (runStateT action) (1, Map.empty) >>= return . fst
+
+-- |A type to represent a formula in Implicative Normal Form.  Such a
+-- formula has the form @a & b & c .=>. d | e | f@, where a thru f are
+-- literals.  One more restriction that is not implied by the type is
+-- that no literal can appear in both the pos set and the neg set.
+data ImplicativeForm lit =
+    INF {neg :: Set.Set lit, pos :: Set.Set lit}
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+-- |A version of MakeINF that takes lists instead of sets, used for
+-- implementing a more attractive show method.
+makeINF' :: (Negatable lit, Ord lit) => [lit] -> [lit] -> ImplicativeForm lit
+makeINF' n p = INF (Set.fromList n) (Set.fromList p)
+
+prettyINF :: (Negatable lit, Ord lit) => (lit -> Doc) -> ImplicativeForm lit -> Doc
+prettyINF lit x = cat $ [text "(", hsep (map lit (Set.toList (neg x))),
+                         text ") => (", hsep (map lit (Set.toList (pos x))), text ")"]
+
+prettyProof :: (Negatable lit, Ord lit) => (lit -> Doc) -> Set.Set (ImplicativeForm lit) -> Doc
+prettyProof lit p = cat $ [text "["] ++ intersperse (text ", ") (map (prettyINF lit) (Set.toList p)) ++ [text "]"]
+
+-- |Take the clause normal form, and turn it into implicative form,
+-- where each clauses becomes an (LHS, RHS) pair with the negated
+-- literals on the LHS and the non-negated literals on the RHS:
+-- @
+--   (a | ~b | c | ~d) becomes (b & d) => (a | c)
+--   (~b | ~d) | (a | c)
+--   ~~(~b | ~d) | (a | c)
+--   ~(b & d) | (a | c)
+-- @
+-- If there are skolem functions on the RHS, split the formula using
+-- this identity:
+-- @
+--   (a | b | c) => (d & e & f)
+-- @
+-- becomes
+-- @
+--    a | b | c => d
+--    a | b | c => e
+--    a | b | c => f
+-- @
+implicativeNormalForm :: forall m formula atom term v f lit. 
+                         (Monad m,
+                          FirstOrderFormula formula atom v,
+                          PropositionalFormula formula atom,
+                          Atom atom term v,
+                          Literal lit atom,
+                          Term term v f,
+                          Data formula, Ord formula, Ord lit, Data lit, Skolem f v) =>
+                         formula -> SkolemT v term m (Set.Set (ImplicativeForm lit))
+implicativeNormalForm formula =
+    do cnf <- clauseNormalForm formula
+       let pairs = Set.map (Set.fold collect (Set.empty, Set.empty)) cnf :: Set.Set (Set.Set lit, Set.Set lit)
+           pairs' = Set.flatten (Set.map split pairs) :: Set.Set (Set.Set lit, Set.Set lit)
+       return (Set.map (\ (n,p) -> INF n p) pairs')
+    where
+      collect :: lit -> (Set.Set lit, Set.Set lit) -> (Set.Set lit, Set.Set lit)
+      collect f (n, p) =
+          foldLiteral (\ f' -> (Set.insert f' n, p))
+                      (ifElse (n, Set.insert true p) (Set.insert true n, p))
+                      (\ _ -> (n, Set.insert f p))
+                      f
+      split :: (Set.Set lit, Set.Set lit) -> Set.Set (Set.Set lit, Set.Set lit)
+      split (lhs, rhs) =
+          if any isSkolem (gFind rhs :: [f])
+          then Set.map (\ x -> (lhs, Set.singleton x)) rhs
+          else Set.singleton (lhs, rhs)
+
+-- | @gFind a@ will extract any elements of type @b@ from
+-- @a@'s structure in accordance with the MonadPlus
+-- instance, e.g. Maybe Foo will return the first Foo
+-- found while [Foo] will return the list of Foos found.
+gFind :: (MonadPlus m, Data a, Typeable b) => a -> m b
+gFind = msum . map return . listify (const True)
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Resolution.hs b/testdata/split-merge-merge-expected/Data/Logic/Resolution.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Resolution.hs
@@ -0,0 +1,373 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeFamilies #-}
+{-# OPTIONS -Wall -Wwarn #-}
+
+{- Resolution.hs -}
+{- Charles Chiou, David Fox -}
+
+module Data.Logic.Resolution
+    ( prove
+    , getSetOfSupport
+    , SetOfSupport
+    , Unification
+    , isRenameOfAtomEq
+    , getSubstAtomEq
+    ) where
+
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Atom (Atom(isRename, getSubst))
+import Data.Logic.Classes.Constants (fromBool)
+import Data.Logic.Classes.Equals (applyEq, AtomEq(foldAtomEq, equals), zipAtomsEq)
+import Data.Logic.Classes.Formula (Formula(atomic))
+import Data.Logic.Classes.Literal (Literal(..), zipLiterals)
+import Data.Logic.Classes.Term (Term(..))
+import Data.Logic.Normal.Implicative (ImplicativeForm(INF, neg, pos))
+import Data.Map (empty, Map)
+import qualified Data.Map as Map (insert, insertWith, intersection, lookup, Map, union, unionWith)
+import Data.Maybe (isJust)
+import qualified Data.Set.Extra as S (any, catMaybes, deleteFindMin, empty, insert, map, minView, null, Set, singleton, size, toList, union, unions)
+
+type SetOfSupport lit v term = S.Set (Unification lit v term)
+
+type Unification lit v term = (ImplicativeForm lit, Map.Map v term)
+
+prove :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, {-Show v, Show term,-} AtomEq atom p term, Predicate p) =>
+         Maybe Int -- ^ Recursion limit.  We continue recursing until this
+                   -- becomes zero.  If it is negative it may recurse until
+                   -- it overflows the stack.
+      -> SetOfSupport lit v term
+      -> SetOfSupport lit v term
+      -> S.Set (ImplicativeForm lit)
+      -> (Bool, SetOfSupport lit v term)
+prove (Just 0) ss1 _ _ = (False, ss1)
+prove limit ss1 ss2' kb  =
+    case S.minView ss2' of
+      Nothing -> (False, ss1)
+      Just (s, ss2) ->
+          case prove' s kb ss2 ss1 of
+            (ss', True) -> (True, (S.insert s (S.union ss1 ss')))
+            (ss', False) -> prove (maybe Nothing (\ n -> Just (n - 1)) limit) (S.insert s ss1) ss' (S.insert (fst s) kb)
+-- prove ss1 [] _kb = (False, ss1)
+-- prove ss1 (s:ss2) kb =
+--     let
+--       (ss', tf) = prove' s kb ss2 ss1
+--     in
+--       if tf then
+--         (True, (ss1 ++ [s] ++ss'))
+--       else
+--         prove (ss1 ++ [s]) ss' (fst s:kb)
+
+prove' :: forall lit atom p f v term.
+          (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+          Unification lit v term -> S.Set (ImplicativeForm lit) -> SetOfSupport lit v term -> SetOfSupport lit v term -> (SetOfSupport lit v term, Bool)
+prove' p kb ss1 ss2 =
+    let
+      res1 = S.map (\x -> resolution p (x, empty)) kb
+      res2 = S.map (\x -> resolution (x, empty) p) kb
+      dem1 = S.map (\e -> demodulate p (e, empty)) kb
+      dem2 = S.map (\p' -> demodulate (p', empty) p) kb
+      (ss', tf) = getResult (S.union ss1 ss2) (S.unions [res1, res2, dem1, dem2])
+    in
+      if S.null ss' then (ss1, False) else (S.union ss1 ss', tf)
+
+getResult :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v, AtomEq atom p term, Eq p) =>
+             SetOfSupport lit v term -> S.Set (Maybe (Unification lit v term)) -> ((SetOfSupport lit v term), Bool)
+getResult ss us =
+    case S.minView us of
+      Nothing ->
+          (S.empty, False)
+      Just (Nothing, xs) ->
+          getResult ss xs
+      Just ((Just x@(inf, _v)), xs) ->
+          if S.null (neg inf) && S.null (pos inf)
+          then (S.singleton x, True)
+          else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+               then getResult ss xs
+               else let (xs', tf) = getResult ss xs in (S.insert x xs', tf)
+{-
+getResult _ [] = (S.empty, False)
+getResult ss (Nothing:xs) = getResult ss xs
+getResult ss ((Just x):xs)  =
+    if S.null (neg inf) && S.null (pos inf)
+    then (S.singleton x, True)
+    else if S.any id (S.map (\(e,_) -> isRenameOf (fst x) e) ss)
+         then getResult ss xs
+         else let (xs', tf) = getResult ss xs in (S.insert x xs' tf)
+    where
+      (inf, _v) = x
+-}
+
+-- |Convert the "question" to a set of support.
+getSetOfSupport :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit, Ord term, Ord v) =>
+                   S.Set (ImplicativeForm lit) -> S.Set (ImplicativeForm lit, Map.Map v term)
+getSetOfSupport s = S.map (\ x -> (x, getSubsts x empty)) s
+
+getSubsts :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+             ImplicativeForm lit -> Map.Map v term -> Map.Map v term
+getSubsts inf theta =
+    getSubstSentences (pos inf) (getSubstSentences (neg inf) theta)
+
+getSubstSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> Map.Map v term -> Map.Map v term
+getSubstSentences xs theta = foldr getSubstSentence theta (S.toList xs)
+
+
+getSubstSentence :: (Literal lit atom, Atom atom term v, Term term v f)  => lit -> Map.Map v term -> Map.Map v term
+getSubstSentence formula theta =
+    foldLiteral
+          (\ s -> getSubstSentence s theta)
+          (const theta)
+          (getSubst theta)
+          formula
+
+getSubstAtomEq :: forall atom p term v f. (AtomEq atom p term, Term term v f) => Map v term -> atom -> Map v term
+getSubstAtomEq theta = foldAtomEq (\ _ ts -> getSubstsTerms ts theta) (const theta) (\ t1 t2 -> getSubstsTerms [t1, t2] theta)
+
+getSubstsTerms :: Term term v f => [term] -> Map.Map v term -> Map.Map v term
+getSubstsTerms [] theta = theta
+getSubstsTerms (x:xs) theta =
+    let
+      theta' = getSubstsTerm x theta
+      theta'' = getSubstsTerms xs theta'
+    in
+      theta''
+
+getSubstsTerm :: Term term v f => term -> Map.Map v term -> Map.Map v term
+getSubstsTerm term theta =
+    foldTerm (\ v -> Map.insertWith (\ _ old -> old) v (vt v) theta)
+             (\ _ ts -> getSubstsTerms ts theta)
+             term
+
+isRenameOf :: (Literal lit atom, Atom atom term v, Term term v f, Ord lit) =>
+              ImplicativeForm lit -> ImplicativeForm lit -> Bool
+isRenameOf inf1 inf2 =
+    (isRenameOfSentences lhs1 lhs2) && (isRenameOfSentences rhs1 rhs2)
+    where
+      lhs1 = neg inf1
+      rhs1 = pos inf1
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+isRenameOfSentences :: (Literal lit atom, Atom atom term v, Term term v f) => S.Set lit -> S.Set lit -> Bool
+isRenameOfSentences xs1 xs2 =
+    S.size xs1 == S.size xs2 && all (uncurry isRenameOfSentence) (zip (S.toList xs1) (S.toList xs2))
+
+isRenameOfSentence :: forall lit atom term v f. (Literal lit atom, Atom atom term v, Term term v f) => lit -> lit -> Bool
+isRenameOfSentence f1 f2 =
+    maybe False id $
+    zipLiterals (\ _ _ -> Just False) (\ x y -> Just (x == y)) (\ x y -> Just (isRename x y)) f1 f2
+
+isRenameOfAtomEq :: (AtomEq atom p term, Term term v f) => atom -> atom -> Bool
+isRenameOfAtomEq a1 a2 =
+    maybe False id $
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> Just (p1 == p2 && isRenameOfTerms ts1 ts2))
+               (\ x y -> Just (x == y))
+               (\ t1l t1r t2l t2r -> Just (isRenameOfTerm t1l t2l && isRenameOfTerm t1r t2r))
+               a1 a2
+
+isRenameOfTerm :: Term term v f => term -> term -> Bool
+isRenameOfTerm t1 t2 =
+    maybe False id $
+    zipTerms (\ _ _ -> Just True)
+             (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && isRenameOfTerms ts1 ts2))
+             t1 t2
+
+isRenameOfTerms :: Term term v f => [term] -> [term] -> Bool
+isRenameOfTerms ts1 ts2 =
+    if length ts1 == length ts2 then
+      let
+        tsTuples = zip ts1 ts2
+      in
+        foldl (&&) True (map (\(t1, t2) -> isRenameOfTerm t1 t2) tsTuples)
+    else
+      False
+
+resolution :: forall lit atom p f term v. (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term, Eq p) =>
+             (ImplicativeForm lit, Map.Map v term) -> (ImplicativeForm lit, Map.Map v term) -> Maybe (ImplicativeForm lit, Map v term)
+resolution (inf1, theta1) (inf2, theta2) =
+    let
+        lhs1 = neg inf1
+        rhs1 = pos inf1
+        lhs2 = neg inf2
+        rhs2 = pos inf2
+        unifyResult = tryUnify rhs1 lhs2
+    in
+      case unifyResult of
+        Just ((rhs1', theta1'), (lhs2', theta2')) ->
+            let
+              lhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') lhs1)
+                              (S.catMaybes $ S.map (\s -> subst s theta2') lhs2')
+              rhs'' = S.union (S.catMaybes $ S.map (\s -> subst s theta1') rhs1')
+                              (S.catMaybes $ S.map (\s -> subst s theta2') rhs2)
+              theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2')
+            in
+              Just (INF lhs'' rhs'', theta)
+        Nothing -> Nothing
+    where
+      tryUnify :: (Literal lit atom, Ord lit) =>
+                  S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify lhs rhs = tryUnify' lhs rhs S.empty
+                         
+      tryUnify' :: (Literal lit atom, Ord lit) =>
+                   S.Set lit -> S.Set lit -> S.Set lit -> Maybe ((S.Set lit, Map.Map v term), (S.Set lit, Map.Map v term))
+      tryUnify' lhss _ _ | S.null lhss = Nothing
+      tryUnify' lhss'' rhss lhss' =
+          let (lhs, lhss) = S.deleteFindMin lhss'' in
+          case tryUnify'' lhs rhss S.empty of
+            Nothing -> tryUnify' lhss rhss (S.insert lhs lhss')
+            Just (rhss', theta1', theta2') ->
+                Just ((S.union lhss' lhss, theta1'), (rhss', theta2'))
+
+      tryUnify'' :: (Literal lit atom, Ord lit) =>
+                    lit -> S.Set lit -> S.Set lit -> Maybe (S.Set lit, Map.Map v term, Map.Map v term)
+      tryUnify'' _x rhss _ | S.null rhss = Nothing
+      tryUnify'' x rhss'' rhss' =
+          let (rhs, rhss) = S.deleteFindMin rhss'' in
+          case unify x rhs of
+            Nothing -> tryUnify'' x rhss (S.insert rhs rhss')
+            Just (theta1', theta2') -> Just (S.union rhss' rhss, theta1', theta2')
+
+-- |Try to unify the second argument using the equality in the first.
+demodulate :: (Literal lit atom, Atom atom term v, Term term v f, Eq lit, Ord lit, Eq term, Ord v, AtomEq atom p term) =>
+              (Unification lit v term) -> (Unification lit v term) -> Maybe (Unification lit v term)
+demodulate (inf1, theta1) (inf2, theta2) =
+    case (S.null (neg inf1), S.toList (pos inf1)) of
+      (True, [lit1]) ->
+          foldLiteral (\ _ -> error "demodulate") (\ _ -> Nothing) (foldAtomEq (\ _ _ -> Nothing) (\ _ -> Nothing) p) lit1
+      _ -> Nothing
+    where
+      p t1 t2 =
+          case findUnify t1 t2 (S.union lhs2 rhs2) of
+            Just ((t1', t2'), theta1', theta2') ->
+                let substNeg2 = S.catMaybes $ S.map (\x -> subst x theta2') lhs2
+                    substPos2 = S.catMaybes $ S.map (\x -> subst x theta2') rhs2
+                    lhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substNeg2
+                    rhs = S.catMaybes $ S.map (\x -> replaceTerm x (t1', t2')) substPos2
+                    theta = Map.unionWith (\ l _r -> l) (updateSubst theta1 theta1') (updateSubst theta2 theta2') in
+                Just (INF lhs rhs, theta)
+            Nothing -> Nothing
+      lhs2 = neg inf2
+      rhs2 = pos inf2
+
+-- |Unification: unifies two sentences.
+unify :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+         lit -> lit -> Maybe (Map.Map v term, Map.Map v term)
+unify s1 s2 = unify' s1 s2 empty empty
+
+unify' :: (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term, Eq p) =>
+          lit -> lit -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unify' f1 f2 theta1 theta2 =
+    zipLiterals
+         (\ _ _ -> error "unify'")
+         (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+         (unify2AtomsEq theta1 theta2)
+         f1 f2
+
+unify2AtomsEq :: (AtomEq atom p term, Term term v f) => Map.Map v term -> Map.Map v term -> atom -> atom -> Maybe (Map.Map v term, Map.Map v term)
+unify2AtomsEq theta1 theta2 a1 a2 =
+    zipAtomsEq (\ p1 ts1 p2 ts2 -> if p1 == p2 then unifyTerms ts1 ts2 theta1 theta2 else Nothing)
+               (\ x y -> if x == y then unifyTerms [] [] theta1 theta2 else Nothing)
+               (\ l1 r1 l2 r2 -> unifyTerms [l1, r1] [l2, r2] theta1 theta2)
+               a1 a2
+
+unifyTerm :: Term term v f => term -> term -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerm t1 t2 theta1 theta2 =
+    foldTerm
+          (\ v1 ->
+               maybe (if vt v1 == t2 then Nothing else Just (Map.insert v1 t2 theta1, theta2))
+                     (\ t1' -> unifyTerm t1' t2 theta1 theta2)
+                     (Map.lookup v1 theta1))
+          (\ f1 ts1 ->
+               foldTerm (\ v2 -> maybe (Just (theta1, Map.insert v2 t1 theta2))
+                                 (\ t2' -> unifyTerm t1 t2' theta1 theta2)
+                                 (Map.lookup v2 theta2))
+                        (\ f2 ts2 -> if f1 == f2
+                                     then unifyTerms ts1 ts2 theta1 theta2
+                                     else Nothing)
+                        t2)
+          t1
+
+unifyTerms :: Term term v f =>
+              [term] -> [term] -> Map.Map v term -> Map.Map v term -> Maybe (Map.Map v term, Map.Map v term)
+unifyTerms [] [] theta1 theta2 = Just (theta1, theta2)
+unifyTerms (t1:ts1) (t2:ts2) theta1 theta2 =
+    case (unifyTerm t1 t2 theta1 theta2) of
+      Nothing                -> Nothing
+      Just (theta1',theta2') -> unifyTerms ts1 ts2 theta1' theta2'
+unifyTerms _ _ _ _ = Nothing
+
+findUnify :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, AtomEq atom p term) =>
+             term -> term -> S.Set lit -> Maybe ((term, term), Map.Map v term, Map.Map v term)
+findUnify tl tr s =
+    let
+      terms = concatMap (foldLiteral (\ (_ :: lit) -> error "getTerms") (\ _ -> []) p) (S.toList s)
+      unifiedTerms' = map (\t -> unifyTerm tl t empty empty) terms
+      unifiedTerms = filter isJust unifiedTerms'
+    in
+     case unifiedTerms of
+       [] -> Nothing
+       (Just (theta1, theta2)):_ ->
+         Just ((substTerm tl theta1, substTerm tr theta1), theta1, theta2)
+       (Nothing:_) -> error "findUnify"
+    where
+      -- getTerms lit = foldLiteral (\ _ -> error "getTerms") p formula
+      p :: atom -> [term]
+      p = foldAtomEq (\ _ ts -> concatMap getTerms' ts) (const []) (\ t1 t2 -> getTerms' t1 ++ getTerms' t2)
+      getTerms' :: term -> [term]
+      getTerms' t = foldTerm (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+
+{-
+getTerms :: Literal formula atom v => formula -> [term]
+getTerms formula =
+    foldLiteral (\ _ -> error "getTerms") p formula
+    where
+      getTerms' t = foldT (\ v -> [vt v]) (\ f ts -> fApp f ts : concatMap getTerms' ts) t
+      p (Equal t1 t2) = getTerms' t1 ++ getTerms' t2
+      p (Apply _ ts) = concatMap getTerms' ts
+-}
+
+replaceTerm :: forall lit atom term v p f. (Literal lit atom, Atom atom term v, Term term v f, Eq term, AtomEq atom p term) => lit -> (term, term) -> Maybe lit
+replaceTerm formula (tl', tr') =
+    foldLiteral
+          (\ _ -> error "error in replaceTerm")
+          (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (map (\ t -> replaceTerm' t) ts))))
+                      (\ x -> Just (atomic (applyEq (fromBool x) [] :: atom)))
+                      (\ t1 t2 -> 
+                           let t1' = replaceTerm' t1
+                               t2' = replaceTerm' t2 in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+    where
+      replaceTerm' t =
+          if termEq t tl'
+          then tr'
+          else foldTerm vt (\ f ts -> fApp f (map replaceTerm' ts)) t
+      termEq t1 t2 =
+          maybe False id (zipTerms (\ v1 v2 -> Just (v1 == v2)) (\ f1 ts1 f2 ts2 -> Just (f1 == f2 && length ts1 == length ts2 && all (uncurry termEq) (zip ts1 ts2))) t1 t2)
+
+subst :: (Literal formula atom, AtomEq atom p term, Atom atom term v, Term term v f, Eq term) => formula -> Map.Map v term -> Maybe formula
+subst formula theta =
+    foldLiteral
+          (\ _ -> Just formula)
+          (\ x -> Just (fromBool x))
+          (foldAtomEq (\ p ts -> Just (atomic (applyEq p (substTerms ts theta))))
+                      (Just . fromBool)
+                      (\ t1 t2 ->
+                           let t1' = substTerm t1 theta
+                               t2' = substTerm t2 theta in
+                           if t1' == t2' then Nothing else Just (atomic (t1' `equals` t2'))))
+          formula
+
+substTerm :: Term term v f => term -> Map.Map v term -> term
+substTerm term theta =
+    foldTerm (\ v -> maybe term id (Map.lookup v theta))
+             (\ f ts -> fApp f (substTerms ts theta))
+             term
+
+substTerms :: Term term v f => [term] -> Map.Map v term -> [term]
+substTerms ts theta = map (\t -> substTerm t theta) ts
+
+updateSubst :: Term term v f => Map.Map v term -> Map.Map v term -> Map.Map v term
+updateSubst theta1 theta2 = Map.union theta1 (Map.intersection theta1 theta2)
+-- This is what was in the original code, which behaves slightly differently
+--updateSubst theta1 _ | Map.null theta1 = Map.empty
+--updateSubst theta1 theta2 = Map.unionWith (\ _ term2 -> term2) theta1 theta2
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Satisfiable.hs b/testdata/split-merge-merge-expected/Data/Logic/Satisfiable.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Satisfiable.hs
@@ -0,0 +1,53 @@
+-- |Do satisfiability computations on any FirstOrderFormula formula by
+-- converting it to a convenient instance of PropositionalFormula and
+-- using the satisfiable function from that instance.  Currently we
+-- use the satisfiable function from the PropLogic package, by the
+-- Bucephalus project.
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, RankNTypes, ScopedTypeVariables #-}
+module Data.Logic.Satisfiable
+    ( satisfiable
+    , theorem
+    , inconsistant
+    , invalid
+    ) where
+
+import Data.Logic.Classes.Atom (Atom)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), toPropositional)
+import Data.Logic.Classes.Literal (Literal)
+import Data.Logic.Classes.Negate ((.~.))
+import Data.Logic.Classes.Propositional (PropositionalFormula)
+import Data.Logic.Classes.Term (Term)
+import Data.Logic.Harrison.Skolem (SkolemT)
+import Data.Logic.Instances.PropLogic ()
+import Data.Logic.Normal.Clause (clauseNormalForm)
+import qualified Data.Set as Set (Set, toList)
+import qualified PropLogic as PL (PropAlg(satisfiable), PropForm(A, CJ, DJ))
+
+-- |Is there any variable assignment that makes the formula true?
+-- satisfiable :: forall formula atom term v f m. (Monad m, FirstOrderFormula formula atom v, Formula atom term v, Term term v f, Ord formula, Literal formula atom v, Ord atom) =>
+--                 formula -> SkolemT v term m Bool
+satisfiable :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                Ord atom, Monad m, Eq formula, Ord formula) =>
+               formula -> SkolemT v term m Bool
+satisfiable f =
+    do (clauses :: Set.Set (Set.Set formula)) <- clauseNormalForm f
+       let f' = PL.CJ . map (PL.DJ . map (toPropositional PL.A)) . map Set.toList . Set.toList $ clauses
+       return . PL.satisfiable $ f'
+
+-- |Is the formula always false?  (Not satisfiable.)
+inconsistant :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                                 Ord atom, Monad m, Eq formula, Ord formula) =>
+                formula -> SkolemT v term m Bool
+inconsistant f =  satisfiable f >>= return . not
+
+-- |Is the negation of the formula inconsistant?
+theorem :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+theorem f = inconsistant ((.~.) f)
+
+-- |A formula is invalid if it is neither a theorem nor inconsistent.
+invalid :: forall m formula atom v term f. (FirstOrderFormula formula atom v, PropositionalFormula formula atom, Literal formula atom, Term term v f, Atom atom term v,
+                                            Ord atom, Monad m, Eq formula, Ord formula) =>
+           formula -> SkolemT v term m Bool
+invalid f = inconsistant f >>= \ fi -> theorem f >>= \ ft -> return (not (fi || ft))
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Common.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Common.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Common.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+module Data.Logic.Types.Common where
+
+import Data.Logic.Classes.Variable (Variable(..))
+import qualified Data.Set as Set
+import Text.PrettyPrint (text)
+
+instance Variable String where
+    variant x vars = if Set.member x vars then variant (x ++ "'") vars else x
+    prefix p x = p ++ x
+    prettyVariable = text
+
+{-
+instance Variable String where
+    variant v vs =
+        if Set.member v vs then variant (next v) (Set.insert v vs) else v
+        where
+          next :: String -> String
+          next s =
+              case break (not . isDigit) (reverse s) of
+                (_, "") -> "x"
+                ("", nondigits) -> nondigits ++ "2"
+                (digits, nondigits) -> nondigits ++ show (1 + read (reverse digits) :: Int)
+-}
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrder.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrder.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
+             GeneralizedNewtypeDeriving, MultiParamTypeClasses, TemplateHaskell, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS -fno-warn-missing-signatures -fno-warn-orphans #-}
+-- |Data types which are instances of the Logic type class for use
+-- when you just want to use the classes and you don't have a
+-- particular representation you need to use.
+module Data.Logic.Types.FirstOrder
+    ( Formula(..)
+    , PTerm(..)
+    , Predicate(..)
+    ) where
+
+import Data.Data (Data)
+import qualified Data.Logic.Classes.Apply as C (Predicate)
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import Data.Logic.Classes.Equals (AtomEq(..), prettyAtomEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder, Quant(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (botFixity, HasFixity(..), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function, Term(..))
+import Data.Logic.Classes.Variable (Variable(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.SafeCopy (base, deriveSafeCopy, extension, MigrateFrom(..), SafeCopy)
+import Data.Typeable (Typeable)
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula v p f
+    = Predicate (Predicate p (PTerm v f))
+    | Combine (Combination (Formula v p f))
+    | Quant Quant v (Formula v p f)
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- |A temporary type used in the fold method to represent the
+-- combination of a predicate and its arguments.  This reduces the
+-- number of arguments to foldFirstOrder and makes it easier to manage the
+-- mapping of the different instances to the class methods.
+data Predicate p term
+    = Equal term term
+    | Apply p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+-- | The range of a term is an element of a set.
+data PTerm v f
+    = Var v                         -- ^ A variable, either free or
+                                    -- bound by an enclosing quantifier.
+    | FunApp f [PTerm v f]           -- ^ Function application.
+                                    -- Constants are encoded as
+                                    -- nullary functions.  The result
+                                    -- is another term.
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+instance Negatable (Formula v p f) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants p => Constants (Formula v p f) where
+    fromBool = Predicate . fromBool
+    asBool (Predicate x) = asBool x
+    asBool _ = Nothing
+
+instance Constants p => Constants (Predicate p (PTerm v f)) where
+    fromBool x = Apply (fromBool x) []
+    asBool (Apply p _) = asBool p
+    asBool _ = Nothing
+
+instance (Constants (Formula v p f) {-, Ord v, Ord p, Ord f-}) => Combinable (Formula v p f) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+instance (C.Predicate p, Function f v) => C.Formula (Formula v p f) (Predicate p (PTerm v f)) where
+    atomic = Predicate
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v, Constants (Formula v p f), Combinable (Formula v p f)
+         ) => PropositionalFormula (Formula v p f) (Predicate p (PTerm v f)) where
+    foldPropositional co tf at formula =
+        maybe testFm tf (asBool formula)
+        where
+          testFm =
+              case formula of
+                Quant _ _ _ -> error "foldF0: quantifiers should not be present"
+                Combine x -> co x
+                Predicate x -> at x
+
+instance (Variable v, Function f v) => Term (PTerm v f) v f where
+    foldTerm vf fn t =
+        case t of
+          Var v -> vf v
+          FunApp f ts -> fn f ts
+    zipTerms v f t1 t2 =
+        case (t1, t2) of
+          (Var v1, Var v2) -> v v1 v2
+          (FunApp f1 ts1, FunApp f2 ts2) -> f f1 ts1 f2 ts2
+          _ -> Nothing
+    vt = Var
+    fApp x args = FunApp x args
+
+{-
+instance (Arity p, Constants p) => Atom (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtom ap (Apply p ts) = ap p ts
+    foldAtom ap (Constant x) = ap (fromBool x) []
+    foldAtom _ _ = error "foldAtom Predicate"
+    zipAtoms ap (Apply p1 ts1) (Apply p2 ts2) = ap p1 ts1 p2 ts2
+    zipAtoms ap (Constant x) (Constant y) = ap (fromBool x) [] (fromBool y) []
+    zipAtoms _ _ _ = error "zipAtoms Predicate"
+    apply' = Apply
+-}
+
+instance C.Predicate p => AtomEq (Predicate p (PTerm v f)) p (PTerm v f) where
+    foldAtomEq ap tf _ (Apply p ts) = maybe (ap p ts) tf (asBool p)
+    foldAtomEq _ _ eq (Equal t1 t2) = eq t1 t2
+    equals = Equal
+    applyEq' = Apply
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          AtomEq (Predicate p (PTerm v f)) p (PTerm v f),
+          Constants (Formula v p f),
+          Variable v, C.Predicate p, Function f v
+         ) => FirstOrderFormula (Formula v p f) (Predicate p (PTerm v f)) v where
+    for_all v x = Quant Forall v x
+    exists v x = Quant Exists v x
+    foldFirstOrder qu co tf at f =
+        maybe testFm tf (asBool f)
+            where testFm = case f of
+                             Quant op v f' -> qu op v f'
+                             Combine x -> co x
+                             Predicate x -> at x
+{-
+    zipFirstOrder qu co tf at f1 f2 =
+        case (f1, f2) of
+          (Quant q1 v1 f1', Quant q2 v2 f2') -> qu q1 v1 (Quant q1 v1 f1') q2 v2 (Quant q2 v2 f2')
+          (Combine x, Combine y) -> co x y
+          (Predicate x, Predicate y) -> at x y
+          _ -> Nothing
+
+instance (Constants (Formula v p f),
+          Variable v, Ord v, Data v, Show v,
+          Arity p, Constants p, Ord p, Data p, Show p,
+          Skolem f, Ord f, Data f, Show f) => Literal (Formula v p f) (Predicate p (PTerm v f)) v where
+    foldLiteral co tf at l =
+        case l of
+          (Combine ((:~:) x)) -> co x
+          (Predicate p) -> at p
+          _ -> error "Literal (Formula v p f)"
+    atomic = Predicate
+-}
+
+instance (Constants p, Ord v, Ord p, Ord f, Constants (Predicate p (PTerm v f)), C.Formula (Formula v p f) (Predicate p (PTerm v f))
+         ) => Literal (Formula v p f) (Predicate p (PTerm v f)) where
+    foldLiteral neg tf at f =
+        case f of
+          Quant _ _ _ -> error "Invalid literal"
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error "Invalid literal"
+          Predicate p -> if p == fromBool True
+                         then tf True
+                         else if p == fromBool False
+                              then tf False
+                              else at p
+
+instance (C.Predicate p, Variable v, Function f v) => C.Atom (Predicate p (PTerm v f)) (PTerm v f) v where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (Apply _ ts) = foldr f r ts
+    foldTerms f r (Equal t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
+
+instance (Variable v, Pretty v,
+          C.Predicate p, Pretty p,
+          Function f v, Pretty f) => Pretty (Predicate p (PTerm v f)) where
+    pretty atom = prettyAtomEq pretty pretty pretty 0 atom
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)),
+          C.Predicate p, Variable v, Function f v, HasFixity (Predicate p (PTerm v f))) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (Predicate p (PTerm v f)), Variable v, C.Predicate p, Function f v) => Pretty (Formula v p f) where
+    pretty f = prettyFirstOrder (\ _ -> pretty) pretty 0 $ f
+
+instance HasFixity (Predicate p term) where
+    fixity = const botFixity
+
+$(deriveSafeCopy 1 'base ''PTerm)
+$(deriveSafeCopy 1 'base ''Formula)
+$(deriveSafeCopy 2 'extension ''Predicate)
+
+-- Migration --
+
+data Predicate_v1 p term
+    = Equal_v1 term term
+    | NotEqual_v1 term term
+    | Constant_v1 Bool
+    | Apply_v1 p [term]
+    deriving (Eq, Ord, Data, Typeable, Show, Read)
+
+$(deriveSafeCopy 1 'base ''Predicate_v1)
+
+instance (SafeCopy p, SafeCopy term) => Migrate (Predicate p term) where
+    type MigrateFrom (Predicate p term) = (Predicate_v1 p term)
+    migrate (Equal_v1 t1 t2) = Equal t1 t2
+    migrate (Apply_v1 p ts) = Apply p ts
+    migrate (NotEqual_v1 _ _) = error "Failure migrating Predicate NotEqual"
+    migrate (Constant_v1 _) = error "Failure migrating Predicate Constant"
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrderPublic.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/FirstOrderPublic.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GeneralizedNewtypeDeriving,
+             MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
+{-# OPTIONS -Wwarn -fno-warn-orphans #-}
+-- |An instance of FirstOrderFormula which implements Eq and Ord by comparing
+-- after conversion to normal form.  This helps us notice that formula which
+-- only differ in ways that preserve identity, e.g. swapped arguments to a
+-- commutative operator.
+
+module Data.Logic.Types.FirstOrderPublic
+    ( Formula(..)
+    , Bijection(..)
+    ) where
+
+import Data.Data (Data)
+import Data.Logic.Classes.Apply (Predicate)
+import Data.Logic.Classes.Combine (Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), fixityFirstOrder, foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder)
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(fixity), Pretty(pretty))
+import Data.Logic.Classes.Propositional (PropositionalFormula(..))
+import Data.Logic.Classes.Term (Function)
+import Data.Logic.Classes.Variable (Variable)
+import Data.Logic.Normal.Implicative (ImplicativeForm, implicativeNormalForm, runNormal)
+import qualified Data.Logic.Types.FirstOrder as N (Formula, Predicate, PTerm)
+import Data.SafeCopy (base, deriveSafeCopy)
+import Data.Set (Set)
+import Data.Typeable (Typeable)
+
+-- |Convert between the public and internal representations.
+class Bijection p i where
+    public :: i -> p
+    intern :: p -> i
+
+-- |The new Formula type is just a wrapper around the Native instance
+-- (which eventually should be renamed the Internal instance.)  No
+-- derived Eq or Ord instances.
+data Formula v p f = Formula {unFormula :: N.Formula v p f} deriving (Data, Typeable, Show)
+
+instance Bijection (Formula v p f) (N.Formula v p f) where
+    public = Formula
+    intern = unFormula
+
+instance Bijection (Combination (Formula v p f)) (Combination (N.Formula v p f)) where
+    public (BinOp x op y) = BinOp (public x) op (public y)
+    public ((:~:) x) = (:~:) (public x)
+    intern (BinOp x op y) = BinOp (intern x) op (intern y)
+    intern ((:~:) x) = (:~:) (intern x)
+
+instance Negatable (Formula v p f) where
+    negatePrivate = Formula . negatePrivate . unFormula
+    foldNegation normal inverted = foldNegation (normal . Formula) (inverted . Formula) . unFormula
+
+instance (Constants (N.Formula v p f), Predicate p, Variable v, Function f v) => Constants (Formula v p f) where
+    fromBool = Formula . fromBool
+    asBool = asBool . unFormula
+
+instance (C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Constants (Formula v p f),
+          Constants (N.Formula v p f),
+          Variable v, Predicate p, Function f v) => Combinable (Formula v p f) where
+    x .<=>. y = Formula $ (unFormula x) .<=>. (unFormula y)
+    x .=>.  y = Formula $ (unFormula x) .=>. (unFormula y)
+    x .|.   y = Formula $ (unFormula x) .|. (unFormula y)
+    x .&.   y = Formula $ (unFormula x) .&. (unFormula y)
+
+instance (Predicate p, Function f v) => C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    atomic = Formula . C.atomic
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Variable v, Predicate p, Function f v) => FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v where
+    for_all v x = public $ for_all v (intern x :: N.Formula v p f)
+    exists v x = public $ exists v (intern x :: N.Formula v p f)
+    foldFirstOrder qu co tf at f = foldFirstOrder qu' co' tf at (intern f :: N.Formula v p f)
+        where qu' quant v form = qu quant v (public form)
+              co' x = co (public x)
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Show v, Show p, Show f, HasFixity (Formula v p f), Variable v, Predicate p,
+          Function f v) => PropositionalFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) where
+    foldPropositional co tf at f = foldPropositional co' tf at (intern f :: N.Formula v p f)
+        where co' x = co (public x)
+
+-- |Here are the magic Ord and Eq instances
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v) => Ord (Formula v p f) where
+    compare a b =
+        let (a' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula a))
+            (b' :: Set (ImplicativeForm (N.Formula v p f))) = runNormal (implicativeNormalForm (unFormula b)) in
+        case compare a' b' of
+          EQ -> EQ
+          x -> {- if isRenameOf a' b' then EQ else -} x
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Predicate p, Function f v, Variable v, Constants (N.Predicate p (N.PTerm v f)),
+          FirstOrderFormula (Formula v p f) (N.Predicate p (N.PTerm v f)) v) => Eq (Formula v p f) where
+    a == b = compare a b == EQ
+
+instance (Predicate p, Function f v) => HasFixity (Formula v p f) where
+    fixity = fixityFirstOrder
+
+instance (C.Formula (Formula v p f) (N.Predicate p (N.PTerm v f)),
+          C.Formula (N.Formula v p f) (N.Predicate p (N.PTerm v f)),
+          Pretty v, Show v, Variable v,
+          Pretty p, Show p, Predicate p,
+          Pretty f, Show f, Function f v) => Pretty (Formula v p f) where
+    pretty formula = prettyFirstOrder (\ _prec a -> pretty a) pretty 0 formula
+
+$(deriveSafeCopy 1 'base ''Formula)
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Equal.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Equal.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Equal.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+module Data.Logic.Types.Harrison.Equal where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+import Data.Logic.Classes.Arity (Arity(..))
+import qualified Data.Logic.Classes.Atom as C (Atom(..))
+import Data.Logic.Classes.Combine (BinOp(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(fromBool))
+import Data.Logic.Classes.Equals (AtomEq(..), showFirstOrderFormulaEq, substAtomEq, varAtomEq)
+import Data.Logic.Classes.FirstOrder (fixityFirstOrder)
+import qualified Data.Logic.Classes.Formula as C (Formula)
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
+import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula(..))
+import Data.Logic.Harrison.Resolution (matchAtomsEq)
+import Data.Logic.Harrison.Tableaux (unifyAtomsEq)
+import Data.Logic.Resolution (getSubstAtomEq, isRenameOfAtomEq)
+import Data.Logic.Types.Harrison.FOL (TermType(..))
+import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import Data.String (IsString(..))
+import Text.PrettyPrint (cat, text)
+
+data FOLEQ = EQUALS TermType TermType | R String [TermType] deriving (Eq, Ord, Show)
+data PredName = (:=:) | Named String deriving (Eq, Ord, Show, Data, Typeable)
+
+instance Arity PredName where
+    arity (:=:) = Just 2
+    arity _ = Nothing
+
+instance Show (Formula FOLEQ) where
+    show = showFirstOrderFormulaEq
+
+instance HasFixity FOLEQ where
+    fixity (EQUALS _ _) = Fixity 5 InfixL
+    fixity _ = Fixity 10 InfixN
+
+instance IsString PredName where
+    fromString "=" = (:=:)
+    fromString s = Named s
+
+instance Constants PredName where
+    fromBool True = Named "true"
+    fromBool False = Named "false"
+    asBool x
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOLEQ where
+    fromBool x = R (fromBool x) []
+    asBool (R p _)
+        | fromBool True == p = Just True
+        | fromBool False == p = Just False
+        | True = Nothing
+    asBool _ = Nothing
+
+instance Predicate PredName
+
+instance Pretty PredName where
+    pretty (:=:) = text "="
+    pretty (Named s) = text s
+
+-- | Using PredName for the predicate type is not quite appropriate
+-- here, but we need to implement this instance so we can use it as a
+-- superclass of AtomEq below.
+instance Apply FOLEQ PredName TermType where
+    foldApply f _ (EQUALS t1 t2) = f (:=:) [t1, t2]
+    foldApply f tf (R p ts) = maybe (f (Named p) ts) tf (asBool (Named p))
+    apply' (Named p) ts = R p ts
+    apply' (:=:) [t1, t2] = EQUALS t1 t2
+    apply' (:=:) _ = error "arity"
+
+{-
+instance FirstOrderFormula (Formula FOLEQ) FOLEQ String where
+    exists = Exists
+    for_all = Forall
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+    atomic = Atom
+-}
+
+instance C.Formula (Formula FOLEQ) FOLEQ => P.PropositionalFormula (Formula FOLEQ) FOLEQ where
+    foldPropositional co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall _ _ -> error "quantifier in propositional formula"
+          Exists _ _ -> error "quantifier in propositional formula"
+
+instance Pretty FOLEQ where
+    pretty (EQUALS a b) = cat [pretty a, pretty (:=:), pretty b]
+    pretty (R s ts) = cat ([pretty s, pretty "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance HasFixity (Formula FOLEQ) where
+    fixity = fixityFirstOrder
+
+instance C.Formula (Formula FOLEQ) FOLEQ => Literal (Formula FOLEQ) FOLEQ where
+    foldLiteral neg tf at lit =
+        case lit of
+          F -> tf False
+          T -> tf True
+          Atom a -> at a
+          Not fm' -> neg fm'
+          _ -> error "Literal (Formula FOLEQ)"
+
+-- instance PredicateEq PredName where
+--     eqp = (:=:)
+
+instance AtomEq FOLEQ PredName TermType where
+    foldAtomEq pr tf _ (R p ts) = maybe (pr (Named p) ts) tf (asBool (Named p))
+    foldAtomEq _ _ eq (EQUALS t1 t2) = eq t1 t2
+    equals = EQUALS
+    applyEq' (Named s) ts = R s ts
+    applyEq' (:=:) [t1, t2] = EQUALS t1 t2
+    applyEq' _ _ = error "arity"
+
+instance C.Atom FOLEQ TermType String where
+    substitute = substAtomEq
+    freeVariables = varAtomEq
+    allVariables = varAtomEq
+    unify = unifyAtomsEq
+    match = matchAtomsEq
+    foldTerms f r (R _ ts) = foldr f r ts
+    foldTerms f r (EQUALS t1 t2) = f t2 (f t1 r)
+    isRename = isRenameOfAtomEq
+    getSubst = getSubstAtomEq
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/FOL.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/FOL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/FOL.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
+module Data.Logic.Types.Harrison.FOL
+    ( TermType(..)
+    , FOL(..)
+    , Function(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.List (intersperse)
+import Data.Logic.Classes.Arity
+import Data.Logic.Classes.Apply (Apply(..), Predicate)
+--import Data.Logic.Classes.Combine (Combination(..), BinOp(..))
+import Data.Logic.Classes.Constants (Constants(fromBool), asBool)
+--import Data.Logic.Classes.FirstOrder (foldAtomsFirstOrder, mapAtomsFirstOrder)
+--import qualified Data.Logic.Classes.Formula as C
+import Data.Logic.Classes.Pretty (Pretty(pretty), HasFixity(..), Fixity(..), FixityDirection(..))
+import Data.Logic.Classes.Skolem (Skolem(..))
+import Data.Logic.Classes.Term (Term(vt, foldTerm, fApp))
+import qualified Data.Logic.Classes.Term as C
+--import qualified Data.Logic.Classes.FirstOrder as C
+--import Data.Logic.Types.Harrison.Formulas.FirstOrder (Formula(..))
+import qualified Data.Logic.Types.Common ({- instance Variable String -})
+import Prelude hiding (pred)
+import Text.PrettyPrint (text, cat)
+
+-- -------------------------------------------------------------------------
+-- Terms.                                                                   
+-- -------------------------------------------------------------------------
+
+data TermType
+    = Var String
+    | Fn Function [TermType]
+    deriving (Eq, Ord)
+
+data FOL = R String [TermType] deriving (Eq, Ord, Show)
+
+instance Show TermType where
+    show (Var v) = "vt " ++ show v
+    show (Fn f ts) = "fApp " ++ show f ++ " " ++ show ts
+
+instance Pretty TermType where
+    pretty (Var v) = pretty v
+    pretty (Fn f ts) = cat ([pretty f, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Apply FOL String TermType where
+    foldApply f tf (R p ts) = maybe (f p ts) tf (asBool p)
+    apply' = R
+
+-- | This is probably dangerous.
+instance Constants String where
+    fromBool True = "true"
+    fromBool False = "false"
+    asBool x 
+        | x == fromBool True = Just True
+        | x == fromBool False = Just False
+        | True = Nothing
+
+instance Constants FOL where
+    fromBool x = R (fromBool x) []
+    asBool (R p _) = asBool p
+
+instance Predicate String
+
+{-
+instance Pretty String where
+    pretty = text
+
+instance FirstOrderFormula (Formula FOL) FOL String where
+    -- type C.Term (Formula FOL) = Term
+    -- type V (Formula FOL) = String
+    -- type Pr (Formula FOL) = String
+    -- type Fn (Formula FOL) = String -- ^ Atomic function type
+
+    -- quant C.Exists v fm = H.Exists v fm
+    -- quant C.Forall v fm = H.Forall v fm
+    for_all = H.Forall
+    exists = H.Exists
+    atomic = Atom
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          H.Forall v fm' -> qu C.Forall v fm'
+          H.Exists v fm' -> qu C.Exists v fm'
+-}
+
+instance Pretty FOL where
+    pretty (R p ts) = cat ([pretty p, text "("] ++ intersperse (text ", ") (map pretty ts) ++ [text ")"])
+
+instance Arity String where
+    arity _ = Nothing
+
+-- | The Harrison book uses String for atomic function, but we need
+-- something a little more type safe because of our Skolem class.
+data Function
+    = FName String
+    | Skolem String
+    deriving (Eq, Ord, Data, Typeable, Show)
+
+instance Pretty Function where
+    pretty (FName s) = text s
+    pretty (Skolem v) = text ("sK" ++ v)
+
+instance C.Function Function String
+
+instance Skolem Function String where
+    toSkolem = Skolem
+    isSkolem (Skolem _) = True
+    isSkolem _ = False
+
+instance Term TermType String Function where
+    -- type V Term = String
+    -- type Fn Term = String
+    vt = Var
+    fApp = Fn
+    foldTerm vfn _ (Var x) = vfn x
+    foldTerm _ ffn (Fn f ts) = ffn f ts
+    zipTerms = undefined
+
+instance HasFixity FOL where
+    fixity = const (Fixity 10 InfixN)
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/FirstOrder.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables,
+             TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.FirstOrder
+    ( Formula(..)
+    ) where
+
+--import Data.Char (isDigit)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..), foldAtomsFirstOrder, mapAtomsFirstOrder, prettyFirstOrder)
+import qualified Data.Logic.Classes.FirstOrder as C (Quant(Exists, Forall))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity, Pretty(pretty))
+import Data.Logic.Types.Common ()
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    | Forall String (Formula a)
+    | Exists String (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Constants a, Pretty a, HasFixity a) => C.Formula (Formula a) a where
+    atomic = Atom
+    foldAtoms = foldAtomsFirstOrder
+    mapAtoms = mapAtomsFirstOrder
+
+instance (C.Formula (Formula a) a, Constants a, Pretty a, HasFixity a) => FirstOrderFormula (Formula a) a String where
+    for_all = Forall
+    exists = Exists
+    foldFirstOrder qu co tf at fm =
+        case fm of
+          F -> tf False
+          T -> tf True
+          Atom atom -> at atom
+          Not fm' -> co ((:~:) fm')
+          And fm1 fm2 -> co (BinOp fm1 (:&:) fm2)
+          Or fm1 fm2 -> co (BinOp fm1 (:|:) fm2)
+          Imp fm1 fm2 -> co (BinOp fm1 (:=>:) fm2)
+          Iff fm1 fm2 -> co (BinOp fm1 (:<=>:) fm2)
+          Forall v fm' -> qu C.Forall v fm'
+          Exists v fm' -> qu C.Exists v fm'
+
+instance (FirstOrderFormula (Formula a) a String) => Pretty (Formula a) where
+    pretty = prettyFirstOrder (const pretty) pretty 0
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Formulas/Propositional.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, DeriveDataTypeable, MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Formulas.Propositional
+    ( Formula(..)
+    ) where
+
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+data Formula a
+    = F
+    | T
+    | Atom a
+    | Not (Formula a)
+    | And (Formula a) (Formula a)
+    | Or (Formula a) (Formula a)
+    | Imp (Formula a) (Formula a)
+    | Iff (Formula a) (Formula a)
+    deriving (Eq, Ord)
+
+instance Negatable (Formula atom) where
+    negatePrivate T = F
+    negatePrivate F = T
+    negatePrivate x = Not x
+    foldNegation normal inverted (Not x) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance Constants (Formula a) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance Combinable (Formula a) where
+    a .<=>. b = Iff a b
+    a .=>. b = Imp a b
+    a .|. b = Or a b
+    a .&. b = And a b
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Combinable (Formula atom), Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    -- The atom type for this formula is the same as its first type parameter.
+    foldPropositional co tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> co ((:~:) f)
+          And f g -> co (BinOp f (:&:) g)
+          Or f g -> co (BinOp f (:|:) g)
+          Imp f g -> co (BinOp f (:=>:) g)
+          Iff f g -> co (BinOp f (:<=>:) g)
+          Atom x -> at x
+
+instance (HasFixity atom, Pretty atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          T -> tf True
+          F -> tf False
+          Not f -> neg f
+          Atom x -> at x
+          _ -> error ("Unexpected literal " ++ show (pretty formula))
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Prop.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Prop.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Harrison/Prop.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses,
+             ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -Wall -Wwarn #-}
+module Data.Logic.Types.Harrison.Prop
+    ( Prop(..)
+    ) where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Pretty
+import Data.Logic.Classes.Propositional (showPropositional)
+import Data.Logic.Types.Harrison.Formulas.Propositional (Formula(..))
+import Prelude hiding (negate)
+import Text.PrettyPrint (text)
+
+-- =========================================================================
+-- Basic stuff for propositional logic: datatype, parsing and printing.     
+-- =========================================================================
+
+newtype Prop = P {pname :: String} deriving (Read, Data, Typeable, Eq, Ord)
+
+instance Show Prop where
+    show x = "P " ++ show (pname x)
+
+instance Pretty Prop where
+    pretty = text . pname
+
+instance HasFixity String where
+    fixity = const botFixity
+
+instance HasFixity Prop where
+    fixity = const botFixity
+
+instance Show (Formula Prop) where
+    show = showPropositional show
+
+instance Show (Formula String) where
+    show = showPropositional show
diff --git a/testdata/split-merge-merge-expected/Data/Logic/Types/Propositional.hs b/testdata/split-merge-merge-expected/Data/Logic/Types/Propositional.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split-merge-merge-expected/Data/Logic/Types/Propositional.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+module Data.Logic.Types.Propositional where
+
+import Data.Generics (Data, Typeable)
+import Data.Logic.Classes.Combine (BinOp(..), Combinable(..), Combination(..))
+import Data.Logic.Classes.Constants (asBool, Constants(..))
+import qualified Data.Logic.Classes.Formula as C (Formula(..))
+import Data.Logic.Classes.Literal (Literal(..))
+import Data.Logic.Classes.Negate (Negatable(..))
+import Data.Logic.Classes.Pretty (HasFixity(..), Pretty(pretty), topFixity)
+import Data.Logic.Classes.Propositional (fixityPropositional, foldAtomsPropositional, mapAtomsPropositional, prettyPropositional, PropositionalFormula(..))
+
+-- | The range of a formula is {True, False} when it has no free variables.
+data Formula atom
+    = Combine (Combination (Formula atom))
+    | Atom atom
+    | T
+    | F
+    -- Note that a derived Eq instance is not going to tell us that
+    -- a&b is equal to b&a, let alone that ~(a&b) equals (~a)|(~b).
+    deriving (Eq,Ord,Data,Typeable)
+
+instance Negatable (Formula atom) where
+    negatePrivate x = Combine ((:~:) x)
+    foldNegation normal inverted (Combine ((:~:) x)) = foldNegation inverted normal x
+    foldNegation normal _ x = normal x
+
+instance (Ord atom) => Combinable (Formula atom) where
+    x .<=>. y = Combine (BinOp  x (:<=>:) y)
+    x .=>.  y = Combine (BinOp  x (:=>:)  y)
+    x .|.   y = Combine (BinOp  x (:|:)   y)
+    x .&.   y = Combine (BinOp  x (:&:)   y)
+
+
+instance Constants (Formula atom) where
+    fromBool True = T
+    fromBool False = F
+    asBool T = Just True
+    asBool F = Just False
+    asBool _ = Nothing
+
+instance (Pretty atom, HasFixity atom, Ord atom) => C.Formula (Formula atom) atom where
+    atomic = Atom
+    foldAtoms = foldAtomsPropositional
+    mapAtoms = mapAtomsPropositional
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Literal (Formula atom) atom where
+    foldLiteral neg tf at formula =
+        case formula of
+          Combine ((:~:) p) -> neg p
+          Combine _ -> error ("Unexpected literal: " ++ show (pretty formula))
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (C.Formula (Formula atom) atom, Pretty atom, HasFixity atom, Ord atom) => PropositionalFormula (Formula atom) atom where
+    foldPropositional co tf at formula =
+        case formula of
+          Combine x -> co x
+          Atom x -> at x
+          T -> tf True
+          F -> tf False
+
+instance (Pretty atom, HasFixity atom, Ord atom) => Pretty (Formula atom) where
+    pretty = prettyPropositional pretty topFixity
+
+instance (Pretty atom, HasFixity atom, Ord atom) => HasFixity (Formula atom) where
+    fixity = fixityPropositional
diff --git a/testdata/split1-expected/Debian/Repo/AptCache.hs b/testdata/split1-expected/Debian/Repo/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/AptCache.hs
@@ -0,0 +1,393 @@
+{- This is a comment at the top -}   
+{-# LANGUAGE DeriveDataTypeable, PackageImports #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |An AptCache represents a local cache of a remote repository.  The
+-- cached information is usually downloaded by running "apt-get
+-- update", and appears in @\/var\/lib\/apt\/lists@.
+module Debian.Repo.AptCache {-# WARNING "this is a warning" #-}
+    ( SourcesChangedAction(..)
+    , aptSourcePackagesSorted
+    , sliceIndexes
+    , cacheDistDir
+    , distDir
+    , aptDir
+    , cacheRootDir
+    , cacheSourcesPath
+    , sourcesPath
+    , sourceDir
+    , aptCacheFiles
+    , aptCacheFilesOfSlice
+    , archFiles
+    , buildArchOfEnv
+    , buildArchOfRoot
+    , updateCacheSources
+    , sourcePackages
+    , binaryPackages
+    , runAptGet
+    , aptOpts
+    , getSourcePackagesBase
+    , getBinaryPackagesBase
+    , getSourcePackagesBuild
+    , getBinaryPackagesBuild
+    ) where
+
+import Control.DeepSeq (force, NFData)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Data (Data)
+import Data.List (intercalate, sortBy)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Relation (BinPkgName, PkgName, SrcPkgName(..))
+import Debian.Release (ReleaseName(relName), releaseName', sectionName')
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.Package.BinaryPackagesOfCachedIndex (binaryPackagesOfCachedIndex)
+import Debian.Repo.Package.SourcePackagesOfCachedIndex (sourcePackagesOfCachedIndex)
+import Debian.Repo.Slice (binarySlices, sourceSlices, verifySourcesList)
+import Debian.Repo.SourcesList (parseSourcesList)
+import Debian.Repo.Types.AptBuildCache (AptBuildCache(aptSliceList))
+import Debian.Repo.Types.AptCache (AptCache(..), AptCache(aptArch, aptBaseSliceList, aptBinaryPackages, aptReleaseName, aptSourcePackages, globalCacheDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(..), EnvRoot(EnvRoot))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID), binaryPackageName, PackageID(packageVersion), PackageIndex(..), SourcePackage(sourcePackageID), sourcePackageName)
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (Repo(repoReleaseInfo), repoKey, RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache, prepareRepository)
+import Debian.Repo.Types.Slice (Slice(..), SliceList(slices))
+import Debian.Sources (DebSource(..), SourceType(..))
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Extra.Files (replaceFile)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetLine, stdin)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Progress (ePutStr, ePutStrLn, qPutStrLn, runProcessF)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+instance NFData ExitCode
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+-- | A directory which will hold all the cached files for this
+-- NamedSliceList.
+cacheDistDir :: FilePath -> ReleaseName -> FilePath
+cacheDistDir cacheDir release = cacheDir ++ "/dists/" ++ relName release
+
+cacheRootDir :: FilePath -> ReleaseName -> EnvRoot
+cacheRootDir cacheDir release = EnvRoot (cacheDistDir cacheDir release ++ "/aptEnv")
+
+distDir :: AptCache c => c -> FilePath
+distDir cache = cacheDistDir (globalCacheDir cache) (aptReleaseName cache)
+
+aptDir :: AptCache c => c -> SrcPkgName -> FilePath
+aptDir cache package = distDir cache ++ "/apt/" ++ unSrcPkgName package
+
+-- | The path where a text of the SliceList is stored.
+cacheSourcesPath :: FilePath -> ReleaseName -> FilePath
+cacheSourcesPath cacheDir release = cacheDistDir cacheDir release </> "sources"
+
+sourcesPath :: AptCache c => c -> FilePath
+sourcesPath cache = cacheSourcesPath (globalCacheDir cache) (aptReleaseName cache)
+
+-- Additional functions which can only be used on already constructed
+-- instances of AptCache.
+
+-- | A directory holding all files downloaded by apt-get source for a
+-- certain package
+sourceDir :: AptCache t => t -> String -> FilePath
+sourceDir c package = distDir c ++ "/apt/" ++ package
+
+-- |Return all the named source packages sorted by version
+aptSourcePackagesSorted :: AptCache t => t -> [SrcPkgName] -> [SourcePackage]
+aptSourcePackagesSorted os names =
+    sortBy cmp . filterNames names . aptSourcePackages $ os
+    where
+      filterNames names' packages =
+          filter (flip elem names' . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: (MonadRepoCache m, AptCache a) => a -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes cache slice =
+    prepareRepository (sliceRepoKey slice) >>= \ repo ->
+    case (sourceDist (sliceSource slice)) of
+      Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+      Right (release, sections) -> return $ map (makeIndex repo release) sections
+    where
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> aptArch cache })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+-- |Return the paths in the local cache of the index files of a slice list.
+aptCacheFiles :: AptCache a => a -> [DebSource] -> [FilePath]
+aptCacheFiles apt sources = concat . map (aptCacheFilesOfSlice apt) $ sources
+
+-- |Return the paths in the local cache of the index files of a single slice.
+aptCacheFilesOfSlice :: AptCache a => a -> DebSource -> [FilePath]
+aptCacheFilesOfSlice apt slice = archFiles (aptArch apt) slice
+
+-- |Return the list of files that apt-get update would write into
+-- \/var\/lib\/apt\/lists when it processed the given list of DebSource.
+archFiles :: Arch -> DebSource -> [FilePath]
+archFiles arch deb =
+    case (arch, deb) of
+      (Binary _ _, DebSource DebSrc _ _) ->
+          map (++ "_source_Sources") (archFiles' deb)
+      (Binary _os _cpu, DebSource Deb _ _) ->
+          map (++ ("_binary-" ++ show (prettyArch arch) ++ "_Packages")) (archFiles' deb)
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+archFiles' :: DebSource -> [FilePath]
+archFiles' deb =
+    let uri = sourceUri deb
+        distro = sourceDist deb in
+    let scheme = uriScheme uri
+        auth = uriAuthority uri
+        path = uriPath uri in
+    let userpass = maybe "" uriUserInfo auth
+        reg = maybeOfString $ maybe "" uriRegName auth
+        port = maybe "" uriPort auth in
+    let (user, pass) = break (== ':') userpass in
+    let user' = maybeOfString user
+        pass' = maybeOfString pass in
+    let uriText = prefix scheme user' pass' reg port path in
+    -- what about dist?
+    either (\ exact -> [(escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText ++ escape exact))])
+           (\ (dist, sections) ->
+                map (\ section ->
+                         (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+                          releaseName' dist ++ "_" ++ sectionName' section))
+                    sections)
+           distro
+    where
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid DebSource: " ++ show (pretty deb))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+
+buildArchOfEnv :: EnvRoot -> IO Arch
+buildArchOfEnv (EnvRoot root)  =
+    do setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+       a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (ArchOS os) (ArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+{-
+  (err, _) <- useEnv root forceList (readProcessChunks (shell cmd) L.empty) >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "export LOGNAME=root; dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+{-
+    do (code, out, err, _) <- runProcess (shell cmd) L.empty >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s =
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.
+updateCacheSources :: (MonadApt m, AptCache c) => SourcesChangedAction -> c -> m c
+updateCacheSources sourcesChangedAction distro =
+    -- (\ x -> qPutStrLn "Updating cache sources" >> quieter 2 x) $
+    qPutStrLn "Updating cache sources" >>
+    do
+      let baseSources = aptBaseSliceList distro
+      --let distro@(ReleaseCache _ dist _) = releaseFromConfig' top text
+      let dir = Debian.Repo.AptCache.distDir distro
+      distExists <- liftIO $ doesFileExist (Debian.Repo.AptCache.sourcesPath distro)
+      case distExists of
+        True ->
+            do
+              fileSources <- liftIO (readFile (Debian.Repo.AptCache.sourcesPath distro)) >>= verifySourcesList Nothing . parseSourcesList
+              case (fileSources == baseSources, sourcesChangedAction) of
+                (True, _) -> return ()
+                (False, SourcesChangedError) ->
+                    do
+                      ePutStrLn ("The sources.list in the existing '" ++ relName (aptReleaseName distro) ++
+                                 "' build environment doesn't match the parameters passed to the autobuilder" ++
+                                 ":\n\n" ++ Debian.Repo.AptCache.sourcesPath distro ++ ":\n\n" ++
+                                 show (pretty fileSources) ++
+                                 "\nRun-time parameters:\n\n" ++
+                                 show (pretty baseSources) ++ "\n" ++
+                                 "It is likely that the build environment in\n" ++
+                                 dir ++ " is invalid and should be rebuilt.")
+                      ePutStr $ "Remove it and continue (or exit)?  [y/n]: "
+                      result <- liftIO $ hGetLine stdin
+                      case result of
+                        ('y' : _) ->
+                            do
+                              liftIO $ removeRecursiveSafely dir
+                              liftIO $ createDirectoryIfMissing True dir
+                              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                        _ ->
+                            error ("Please remove " ++ dir ++ " and restart.")
+                (False, RemoveRelease) ->
+                    do
+                      ePutStrLn $ "Removing suspect environment: " ++ dir
+                      liftIO $ removeRecursiveSafely dir
+                      liftIO $ createDirectoryIfMissing True dir
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                (False, UpdateSources) ->
+                    do
+                      -- The sources.list has changed, but it should be
+                      -- safe to update it.
+                      ePutStrLn $ "Updating environment with new sources.list: " ++ dir
+                      liftIO $ removeFile (Debian.Repo.AptCache.sourcesPath distro)
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+        False ->
+            do
+              liftIO $ createDirectoryIfMissing True dir
+              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+      return distro
+
+-- | Return a sorted list of available source packages, newest version first.
+sourcePackages :: AptCache a => a -> [SrcPkgName] -> [SourcePackage]
+sourcePackages os names =
+    sortBy cmp . filterNames . aptSourcePackages $ os
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          filter (flip elem names . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+binaryPackages :: AptCache a => a -> [BinPkgName] -> [BinaryPackage]
+binaryPackages os names =
+    sortBy cmp . filterNames . aptBinaryPackages $ os
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          filter (flip elem names . binaryPackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
+
+getSourcePackagesBase :: (AptCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getSourcePackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBase :: (AptCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+-- | Note that apt-get source works for binary or source package names.
+runAptGet :: (PkgName n, AptCache t) => t -> FilePath -> String -> [(n, Maybe DebianVersion)] -> IO ()
+runAptGet os dir command packages =
+    createDirectoryIfMissing True dir >> runProcessF (shell cmd) L.empty >> return ()
+    where
+      cmd = (intercalate " " ("cd" : dir : "&&" : "apt-get" : aptOpts os : command : map formatPackage packages))
+      formatPackage (name, Nothing) = show (pretty name)
+      formatPackage (name, Just version) = show (pretty name) ++ "=" ++ show (prettyDebianVersion version)
+
+aptOpts :: AptCache t => t -> String
+aptOpts os =
+    (" -o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status" ++
+     " -o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists" ++
+     " -o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives" ++
+     " -o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list" ++
+     " -o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d")
+    where root = rootPath . rootDir $ os
diff --git a/testdata/split1-expected/Debian/Repo/AptImage.hs b/testdata/split1-expected/Debian/Repo/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/AptImage.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |The AptImage object represents a partial OS image which is capable
+-- of running apt-get, and thus obtaining repository info and source
+-- code packages.
+module Debian.Repo.AptImage
+    ( prepareAptEnv
+    , updateAptEnv
+    ) where
+
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Function (on)
+import Data.List (sortBy)
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.AptCache (aptOpts, buildArchOfRoot, cacheRootDir, getBinaryPackagesBase, getSourcePackagesBase, SourcesChangedAction, updateCacheSources)
+import Debian.Repo.Monads.Apt (insertAptImage, lookupAptImage, MonadApt(getApt, putApt))
+import Debian.Repo.Types.AptImage (AptImage(..))
+import Debian.Repo.Types.EnvPath (EnvRoot(..))
+import Debian.Repo.Types.PackageIndex (PackageID(packageVersion), SourcePackage(sourcePackageID))
+import Debian.Repo.Types.Slice (NamedSliceList(sliceList, sliceListName))
+import Debian.Sources (SliceName(sliceName))
+import Extra.Files (replaceFile, writeFileIfMissing)
+import System.Directory (createDirectoryIfMissing)
+import System.Process (shell)
+import System.Process.Progress (qPutStrLn, quieter, runProcessF)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+prepareAptEnv :: MonadApt m =>
+                 FilePath               -- Put environment in a subdirectory of this
+              -> SourcesChangedAction   -- What to do if environment already exists and sources.list is different
+              -> NamedSliceList         -- The sources.list
+              -> m AptImage             -- The resulting environment
+prepareAptEnv cacheDir sourcesChangedAction sources =
+    (\ x -> qPutStrLn ("Preparing apt-get environment for " ++ show (sliceName (sliceListName sources))) >> quieter 2 x) $
+    getApt >>= return . lookupAptImage (sliceListName sources) >>=
+    maybe (prepareAptEnv' cacheDir sourcesChangedAction sources) return
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+{-# NOINLINE prepareAptEnv' #-}
+prepareAptEnv' :: MonadApt m => FilePath -> SourcesChangedAction -> NamedSliceList -> m AptImage
+prepareAptEnv' cacheDir sourcesChangedAction sources =
+    do let root = rootPath (cacheRootDir cacheDir (ReleaseName (sliceName (sliceListName sources))))
+       --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/cache/apt/archives/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/dpkg")
+       liftIO $ createDirectoryIfMissing True (root ++ "/etc/apt")
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/status") ""
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/diversions") ""
+       -- We need to create the local pool before updating so the
+       -- sources.list will be valid.
+       let sourceListText = show (pretty (sliceList sources))
+       -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+       liftIO $ replaceFile (root ++ "/etc/apt/sources.list") sourceListText
+       arch <- liftIO $ buildArchOfRoot
+       let os = AptImage { aptGlobalCacheDir = cacheDir
+                         , aptImageRoot = EnvRoot root
+                         , aptImageArch = arch
+                         , aptImageReleaseName = ReleaseName . sliceName . sliceListName $ sources
+                         , aptImageSliceList = sliceList sources
+                         , aptImageSourcePackages = []
+                         , aptImageBinaryPackages = [] }
+       os' <- updateCacheSources sourcesChangedAction os >>= updateAptEnv
+       getApt >>= putApt . insertAptImage (sliceListName sources) os'
+       return os'
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+{-# NOINLINE updateAptEnv #-}
+updateAptEnv :: MonadApt m => AptImage -> m AptImage
+updateAptEnv os =
+    liftIO (runProcessF (shell cmd) L.empty) >>
+    getSourcePackagesBase os >>= return . sortBy cmp >>= \ sourcePackages ->
+    getBinaryPackagesBase os >>= \ binaryPackages ->
+    return $ os { aptImageSourcePackages = sourcePackages
+                , aptImageBinaryPackages = binaryPackages }
+    where
+      cmd = "apt-get" ++ aptOpts os ++ " update"
+      -- Flip args to get newest version first
+      cmp = flip (compare `on` (packageVersion . sourcePackageID))
+{-
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+    putStrLn ("> " ++ cmd) >> system cmd >>= \ code ->
+    case code of
+      ExitSuccess -> return ()
+      ExitFailure n -> error $ cmd ++ " -> ExitFailure " ++ show n
+-}
diff --git a/testdata/split1-expected/Debian/Repo/Monads/Apt.hs b/testdata/split1-expected/Debian/Repo/Monads/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Monads/Apt.hs
@@ -0,0 +1,195 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Debian.Repo.Monads.Apt
+    ( MonadApt(..)
+    , AptIOT
+    , AptIO
+    -- * AptIO Monad
+    , runAptIO
+    , runAptT
+    , tryAB
+    , tryJustAB
+    -- * State
+    , AptState
+    , initState
+    , setRepoMap
+    , getRepoMap
+    , lookupRepository
+    , insertRepository
+    , lookupAptImage
+    , insertAptImage
+    , lookupSourcePackages
+    , insertSourcePackages
+    , lookupBinaryPackages
+    , insertBinaryPackages
+    , readParagraphs
+    , findRelease
+    , putRelease
+    , countTasks
+    ) where
+
+import Control.Exception (try)
+import Control.Exception as E (Exception, tryJust)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, mapStateT, MonadIO(..), MonadTrans(..), put, StateT(runStateT))
+import qualified Data.Map as Map (empty, insert, lookup, Map)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(parseControlFromHandle), Paragraph)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Repo (Repo(repoKey), RepoKey(..))
+import Debian.Repo.Types.Repository (MonadRepoCache(getRepoCache, putRepoCache), Repository)
+import Debian.Sources (SliceName)
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+import System.Process.Progress (ePutStrLn)
+import Text.Printf (printf)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+type AptIO = AptIOT IO
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository           -- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage       -- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage]) -- ^ The contents of a package index, and status of local cache of index file
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+-- |Perform an AptIO monad task in the IO monad.
+runAptIO :: AptIO a -> IO a
+runAptIO action = (runStateT action) initState >>= \ (a, _) -> return a
+
+runAptT :: Monad m => AptIOT m a -> m a
+runAptT action = (runStateT action) initState >>= return . fst
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryAB :: Exception e => AptIO a -> AptIO (Either e a)
+tryAB task =
+    do state <- get
+       mapStateT (try' state) task
+    where
+      try' state task' =
+          do result <- try task'
+             case result of
+               Left e -> return (Left e, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryJustAB :: Exception e => (e -> Maybe b) -> AptIO a -> AptIO (Either b a)
+tryJustAB f task =
+    do state <- get
+       mapStateT (tryJust' state) task
+    where
+      tryJust' state task' =
+          do result <- tryJust f task'
+             case result of
+               Left b -> return (Left b, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+setRepoMap :: Map.Map RepoKey Repository -> AptState -> AptState
+setRepoMap m state = state {repoMap = m}
+
+getRepoMap :: AptState -> Map.Map RepoKey Repository
+getRepoMap state = repoMap state
+
+lookupRepository :: RepoKey -> AptState -> Maybe Repository
+lookupRepository uri state = Map.lookup uri (repoMap state)
+
+insertRepository :: RepoKey -> Repository -> AptState -> AptState
+insertRepository uri repo state = state {repoMap = Map.insert uri repo (repoMap state)}
+
+lookupAptImage :: SliceName -> AptState -> Maybe AptImage
+lookupAptImage name state = Map.lookup  name (aptImageMap state)
+
+insertAptImage :: SliceName -> AptImage -> AptState -> AptState
+insertAptImage name image state = state {aptImageMap = Map.insert name image (aptImageMap state)}
+
+lookupSourcePackages :: FilePath -> AptState -> Maybe (FileStatus, [SourcePackage])
+lookupSourcePackages key state = Map.lookup key (sourcePackageMap state)
+
+insertSourcePackages :: FilePath -> (FileStatus, [SourcePackage]) -> AptState -> AptState
+insertSourcePackages key packages state = state {sourcePackageMap = Map.insert key packages (sourcePackageMap state)}
+
+lookupBinaryPackages :: FilePath -> AptState -> Maybe (FileStatus, [BinaryPackage])
+lookupBinaryPackages key state = Map.lookup key (binaryPackageMap state)
+
+insertBinaryPackages :: FilePath -> (FileStatus, [BinaryPackage]) -> AptState -> AptState
+insertBinaryPackages key packages state =
+    state {binaryPackageMap = Map.insert key packages (binaryPackageMap state)}
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)                 -- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")     -- Debugging output
+       return paragraphs
+
+findRelease :: Repository -> ReleaseName -> AptState -> Maybe Release
+findRelease repo dist state =
+    Map.lookup (repoKey repo, dist) (releaseMap state)
+
+putRelease :: Repository -> ReleaseName -> Release -> AptState -> AptState
+putRelease repo dist release state =
+    state {releaseMap = Map.insert (repoKey repo, dist) release (releaseMap state)}
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/split1-expected/Debian/Repo/Monads/Top.hs b/testdata/split1-expected/Debian/Repo/Monads/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Monads/Top.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Monads.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    ) where
+
+import Control.Monad.Reader (MonadReader(ask), ReaderT(runReaderT))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class Monad m => MonadTop m where
+    askTop :: m FilePath
+
+instance Monad m => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
diff --git a/testdata/split1-expected/Debian/Repo/Orphans.hs b/testdata/split1-expected/Debian/Repo/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Orphans.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Orphans where
+
+import Data.Text (Text)
+import qualified Debian.Control.Text as T
+
+deriving instance Show (T.Field' Text)
+deriving instance Ord (T.Field' Text)
+deriving instance Show T.Paragraph
+deriving instance Ord T.Paragraph
diff --git a/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceID.hs b/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceID.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceID.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.BinaryPackageSourceID
+    ( binaryPackageSourceID
+    ) where
+
+import qualified Data.Text as T (unpack)
+import qualified Debian.Control.Text as B (fieldValue)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID, packageInfo), makeBinaryPackageID, PackageID(..), PackageIndex(PackageIndex))
+import Debian.Version (parseDebianVersion)
+import Text.Regex (matchRegex, mkRegex)
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID (PackageIndex component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
diff --git a/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceVersion.hs b/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceVersion.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/BinaryPackageSourceVersion.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.BinaryPackageSourceVersion
+    ( binaryPackageSourceVersion
+    ) where
+
+import Debian.Repo.Package.Internal.BinarySourceVersion (binarySourceVersion')
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID, packageInfo), binaryPackageName, PackageID(packageVersion))
+import Debian.Version (DebianVersion)
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = binaryPackageName package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
diff --git a/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfCachedIndex.hs b/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfCachedIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfCachedIndex.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.BinaryPackagesOfCachedIndex
+    ( binaryPackagesOfCachedIndex
+    ) where
+
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import Data.List as List (map)
+import Debian.Repo.Monads.Apt (insertBinaryPackages, lookupBinaryPackages, MonadApt(..), readParagraphs)
+import Debian.Repo.Package.Internal.IndexCacheFile (indexCacheFile)
+import Debian.Repo.Package.ToBinaryPackage (toBinaryPackage)
+import Debian.Repo.Types.AptCache (AptCache(rootDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.Types.PackageIndex (BinaryPackage, PackageIndex)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfCachedIndex :: (MonadApt m, AptCache a) => a -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupBinaryPackages path state
+       status <- liftIO $ getFileStatus path
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toBinaryPackage release index) paragraphs
+                 putApt (insertBinaryPackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
diff --git a/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfIndex.hs b/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/BinaryPackagesOfIndex.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.BinaryPackagesOfIndex
+    ( binaryPackagesOfIndex
+    ) where
+
+import Control.Exception as E (SomeException)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import Debian.Arch (Arch(Source))
+import Debian.Repo.Package.GetPackages (getPackages)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, PackageIndex(packageIndexArch))
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache)
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index 
diff --git a/testdata/split1-expected/Debian/Repo/Package/BinarySourceVersion.hs b/testdata/split1-expected/Debian/Repo/Package/BinarySourceVersion.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/BinarySourceVersion.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.BinarySourceVersion
+    ( binarySourceVersion
+    ) where
+
+import qualified Data.Text as T (Text, unpack)
+import Debian.Control (Paragraph')
+import qualified Debian.Control.Text as B (fieldValue)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Package.Internal.BinarySourceVersion (binarySourceVersion')
+import Debian.Version (DebianVersion, parseDebianVersion)
+
+-- |Return the name and version number of the source package that
+-- generated this binary package.
+-- see also: 'binaryPackageSourceVersion'
+binarySourceVersion :: Paragraph' T.Text -> Maybe ((BinPkgName, DebianVersion), (String, DebianVersion))
+binarySourceVersion paragraph =
+    let mBinaryName = fmap (BinPkgName . T.unpack) $ B.fieldValue "Package" paragraph
+        mBinaryVersion = fmap (parseDebianVersion . T.unpack) $ B.fieldValue "Version" paragraph
+    in
+      case (mBinaryName, mBinaryVersion) of
+        (Just binaryName, Just binaryVersion) ->
+            fmap ((,) (binaryName, binaryVersion)) $ binarySourceVersion' binaryName binaryVersion paragraph
+        _ -> Nothing
diff --git a/testdata/split1-expected/Debian/Repo/Package/GetPackages.hs b/testdata/split1-expected/Debian/Repo/Package/GetPackages.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/GetPackages.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.GetPackages
+    ( getPackages
+    ) where
+
+import Control.Exception as E (ErrorCall(ErrorCall), SomeException(..), try)
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString)
+import Data.List as List (map)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import qualified Debian.Control.Text as B (Control'(Control))
+import Debian.Repo.Package.ToBinaryPackage (toBinaryPackage)
+import Debian.Repo.PackageIndex (packageIndexPath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, PackageIndex)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey, repoKeyURI)
+import Debian.URI (fileFromURIStrict)
+import Network.URI (URI(uriPath))
+import System.FilePath ((</>))
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/BinarySourceVersion.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/BinarySourceVersion.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/BinarySourceVersion.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.BinarySourceVersion
+    ( binarySourceVersion'
+    ) where
+
+import Debian.Control (ControlFunctions(asString), Paragraph')
+import qualified Debian.Control.Text as B (fieldValue)
+import Debian.Relation (BinPkgName(..))
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Text.Regex (matchRegex, mkRegex)
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/Constants.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/Constants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/Constants.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.Constants
+    ( Constants(asBool, fromBool)
+    ) where
+
+
+
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/IndexCacheFile.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/IndexCacheFile.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/IndexCacheFile.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.IndexCacheFile
+    ( indexCacheFile
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Repo.Package.Internal.IndexPrefix (indexPrefix)
+import Debian.Repo.Types.AptCache (AptCache(aptArch))
+import Debian.Repo.Types.PackageIndex (PackageIndex(packageIndexArch))
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/IndexPrefix.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/IndexPrefix.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/IndexPrefix.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.IndexPrefix
+    ( indexPrefix
+    ) where
+
+import Data.List as List (intercalate)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Package.Internal.OtherSymbols ((+?+))
+import Debian.Repo.Package.Internal.UriToString (uriToString')
+import Debian.Repo.Types.PackageIndex (PackageIndex(packageIndexComponent))
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (RepoKey, repoKeyURI)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/OtherSymbols.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/OtherSymbols.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/OtherSymbols.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.OtherSymbols
+    ( (+?+)
+    ) where
+
+
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/ParseSourceParagraph.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/ParseSourceParagraph.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/ParseSourceParagraph.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.ParseSourceParagraph
+    ( parseSourceParagraph
+    ) where
+
+import Debian.Control (ControlFunctions(stripWS))
+import qualified Debian.Control.Text as B (fieldValue, Paragraph)
+import Debian.Repo.Types.PackageIndex (SourceControl(..))
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/TryParseRel.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/TryParseRel.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/TryParseRel.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.TryParseRel
+    ( tryParseRel
+    ) where
+
+import qualified Debian.Control.Text as B (Field, Field'(Field))
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/UriToString.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/UriToString.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/UriToString.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.UriToString
+    ( uriToString'
+    ) where
+
+import Network.URI (URI, uriToString)
+
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
diff --git a/testdata/split1-expected/Debian/Repo/Package/Internal/UseConstants.hs b/testdata/split1-expected/Debian/Repo/Package/Internal/UseConstants.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/Internal/UseConstants.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.Internal.UseConstants
+    ( useConstants
+    ) where
+
+import Debian.Repo.Package.Internal.Constants (Constants(asBool))
+
+useConstants :: Constants p => p -> Maybe Bool
+useConstants x = asBool x
diff --git a/testdata/split1-expected/Debian/Repo/Package/PutPackages.hs b/testdata/split1-expected/Debian/Repo/Package/PutPackages.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/PutPackages.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.PutPackages
+    ( putPackages
+    ) where
+
+import qualified Data.ByteString.Lazy.Char8 as L (fromChunks)
+import Data.List as List (intercalate, intersperse, map)
+import qualified Data.Text as T (concat, pack)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Control (formatParagraph)
+import Debian.Repo.PackageIndex (packageIndexPath)
+import Debian.Repo.Types.EnvPath (outsidePath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageInfo), BinaryPackageLocal, PackageIndexLocal)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repository (LocalRepository, repoRoot)
+import Extra.Files (writeAndZipFileWithBackup)
+import System.FilePath ((</>))
+
+-- | Write a set of packages into a package index.
+putPackages :: LocalRepository -> Release -> PackageIndexLocal ->  [BinaryPackageLocal] -> IO ()
+putPackages repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
diff --git a/testdata/split1-expected/Debian/Repo/Package/ReExported.hs b/testdata/split1-expected/Debian/Repo/Package/ReExported.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/ReExported.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.ReExported
+    ( TH.FixityDirection(..)
+    , TH.Fixity(..)
+    ) where
+
+import qualified Language.Haskell.TH.Syntax as TH (Fixity(..), FixityDirection(..))
diff --git a/testdata/split1-expected/Debian/Repo/Package/ReleaseBinaryPackages.hs b/testdata/split1-expected/Debian/Repo/Package/ReleaseBinaryPackages.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/ReleaseBinaryPackages.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.ReleaseBinaryPackages
+    ( releaseBinaryPackages
+    ) where
+
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, map)
+import Data.Set as Set (fromList, Set, unions)
+import Debian.Repo.Package.BinaryPackagesOfIndex (binaryPackagesOfIndex)
+import Debian.Repo.PackageIndex (binaryIndexList)
+import Debian.Repo.Types.PackageIndex (BinaryPackage)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages :: MonadRepoCache m => RepoKey -> Release -> m (Set BinaryPackage)
+releaseBinaryPackages repo release =
+    mapM (binaryPackagesOfIndex repo release) (binaryIndexList release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
diff --git a/testdata/split1-expected/Debian/Repo/Package/ReleaseSourcePackages.hs b/testdata/split1-expected/Debian/Repo/Package/ReleaseSourcePackages.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/ReleaseSourcePackages.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.ReleaseSourcePackages
+    ( releaseSourcePackages
+    ) where
+
+import Control.Exception as E (SomeException)
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, map)
+import Data.Set as Set (fromList, Set, unions)
+import Debian.Repo.Package.SourcePackagesOfIndex (sourcePackagesOfIndex)
+import Debian.Repo.PackageIndex (sourceIndexList)
+import Debian.Repo.Types.PackageIndex (SourcePackage)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache)
+
+-- | Return a list of all source packages.
+releaseSourcePackages :: MonadRepoCache m => RepoKey -> Release -> m (Set SourcePackage)
+releaseSourcePackages repo release =
+    mapM (sourcePackagesOfIndex repo release) (sourceIndexList release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourceBinaryNames.hs b/testdata/split1-expected/Debian/Repo/Package/SourceBinaryNames.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourceBinaryNames.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourceBinaryNames
+    ( sourceBinaryNames
+    ) where
+
+import Data.List as List (map)
+import qualified Data.Text as T (unpack)
+import Debian.Control (formatParagraph)
+import qualified Debian.Control.Text as B (fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+sourceBinaryNames paragraph =
+    case B.fieldValue "Binary" paragraph of
+      Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourceFilePaths.hs b/testdata/split1-expected/Debian/Repo/Package/SourceFilePaths.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourceFilePaths.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourceFilePaths
+    ( sourceFilePaths
+    ) where
+
+import Data.Set as Set (fromList, map, Set)
+import Debian.Repo.Types.PackageIndex (SourceFileSpec(sourceFileName), SourcePackage(sourceDirectory, sourcePackageFiles))
+import System.FilePath ((</>))
+
+sourceFilePaths :: SourcePackage -> Set FilePath
+sourceFilePaths package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryIDs.hs b/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryIDs.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryIDs.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourcePackageBinaryIDs
+    ( sourcePackageBinaryIDs
+    ) where
+
+import Data.List as List (map)
+import qualified Data.Text as T (unpack)
+import Debian.Arch (Arch(Source))
+import Debian.Control (formatParagraph)
+import qualified Debian.Control.Text as B (fieldValue)
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Types.PackageIndex (makeBinaryPackageID, PackageID, PackageIndex, SourcePackage(sourceParagraph))
+import Debian.Version (parseDebianVersion)
+import Text.Regex (mkRegex, splitRegex)
+
+sourcePackageBinaryIDs :: Arch -> PackageIndex -> SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs Source _ _ = error "invalid argument"
+sourcePackageBinaryIDs arch sourceIndex package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryNames.hs b/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryNames.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourcePackageBinaryNames.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourcePackageBinaryNames
+    ( sourcePackageBinaryNames
+    ) where
+
+import Debian.Relation (BinPkgName(..))
+import Debian.Repo.Package.SourceBinaryNames (sourceBinaryNames)
+import Debian.Repo.Types.PackageIndex (SourcePackage(sourceParagraph))
+
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfCachedIndex.hs b/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfCachedIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfCachedIndex.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourcePackagesOfCachedIndex
+    ( sourcePackagesOfCachedIndex
+    ) where
+
+import Control.Exception as E (catch)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import Data.List as List (map)
+import Debian.Repo.Monads.Apt (insertSourcePackages, lookupSourcePackages, MonadApt(..), readParagraphs)
+import Debian.Repo.Package.Internal.IndexCacheFile (indexCacheFile)
+import Debian.Repo.Package.ToSourcePackage (toSourcePackage)
+import Debian.Repo.Types.AptCache (AptCache(rootDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(rootPath))
+import Debian.Repo.Types.PackageIndex (PackageIndex, SourcePackage)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import System.FilePath (takeDirectory)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfCachedIndex :: (AptCache a, MonadApt m) => a -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupSourcePackages path state
+       status <- liftIO $ getFileStatus path `E.catch` (\ (_ :: IOError) -> error $ "Sources.list seems out of sync.  If a new release has been created you probably need to remove " ++ takeDirectory (rootPath (rootDir cache)) ++ " and try again - sorry about that.")
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toSourcePackage index) paragraphs
+                 putApt (insertSourcePackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
diff --git a/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfIndex.hs b/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/SourcePackagesOfIndex.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.SourcePackagesOfIndex
+    ( sourcePackagesOfIndex
+    ) where
+
+import Control.Exception as E (SomeException)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import Data.List as List (map)
+import Debian.Arch (Arch(Source))
+import Debian.Repo.Package.GetPackages (getPackages)
+import Debian.Repo.Package.ToSourcePackage (toSourcePackage)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageInfo), PackageIndex(packageIndexArch), SourcePackage)
+import Debian.Repo.Types.Release (Release)
+import Debian.Repo.Types.Repo (RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache)
+-- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
diff --git a/testdata/split1-expected/Debian/Repo/Package/ToBinaryPackage.hs b/testdata/split1-expected/Debian/Repo/Package/ToBinaryPackage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/ToBinaryPackage.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.ToBinaryPackage
+    ( toBinaryPackage
+    ) where
+
+import qualified Data.Text as T (unpack)
+import qualified Debian.Control.Text as B (ControlFunctions(lookupP), fieldValue, Paragraph)
+import Debian.Repo.Package.Internal.TryParseRel (tryParseRel)
+import Debian.Repo.PackageIndex (packageIndexPath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(..), makeBinaryPackageID, PackageIndex)
+import Debian.Repo.Types.Release (Release)
+import Debian.Version (parseDebianVersion)
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
diff --git a/testdata/split1-expected/Debian/Repo/Package/ToSourcePackage.hs b/testdata/split1-expected/Debian/Repo/Package/ToSourcePackage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Package/ToSourcePackage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package.ToSourcePackage
+    ( toSourcePackage
+    ) where
+
+import Data.List as List (intercalate, map, partition)
+import Data.Maybe (catMaybes)
+import qualified Data.Text as T (Text, unpack)
+import Debian.Control (formatParagraph)
+import qualified Debian.Control.Text as B (fieldValue, Paragraph)
+import Debian.Repo.Package.Internal.ParseSourceParagraph (parseSourceParagraph)
+import Debian.Repo.Types.PackageIndex (makeSourcePackageID, PackageIndex, SourceFileSpec(SourceFileSpec), SourcePackage(..))
+import Debian.Version (parseDebianVersion)
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
diff --git a/testdata/split1-expected/Debian/Repo/PackageIndex.hs b/testdata/split1-expected/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,87 @@
+module Debian.Repo.PackageIndex
+    ( packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPathList
+    , packageIndexDirList
+    , packageIndexList
+    , sourceIndexList
+    , binaryIndexList
+    , releaseDir
+    , showIndexBrief
+    , debSourceFromIndex
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Types (PackageIndex(..), Release(..))
+import Debian.Repo.Types.Repo (repoURI)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (DebSource(..), SourceType(..))
+import System.FilePath ((</>))
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index ++ "/" ++ packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release ++ "/" ++ sectionName' (packageIndexComponent index) ++ "/source"
+      _ -> (releaseDir release ++ "/" ++
+            sectionName' (packageIndexComponent index) ++
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "/binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists/" ++ (releaseName' . releaseName $ release)
+
+packageIndexPathList :: Release -> [FilePath]
+packageIndexPathList release = map (packageIndexPath release) . packageIndexList $ release
+
+packageIndexDirList :: Release -> [FilePath]
+packageIndexDirList release = map (packageIndexDir release) . packageIndexList $ release
+
+packageIndexList :: Release -> [PackageIndex]
+packageIndexList release = sourceIndexList release ++ binaryIndexList release
+
+sourceIndexList :: Release -> [PackageIndex]
+sourceIndexList release =
+    map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexList :: Release -> [PackageIndex]
+binaryIndexList release =
+    concat . map componentIndexes $ (releaseComponents release)
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          map archIndex (filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+showIndexBrief :: (Repository, Release) -> PackageIndex -> String
+showIndexBrief release index =
+    (releaseName' . releaseName . snd $ release) </> sectionName' (packageIndexComponent index) </> showArch (packageIndexArch index)
+    where showArch Source = "source"
+          showArch All = "all"
+          showArch x@(Binary _ _) = "binary-" ++ show (prettyArch x)
+
+debSourceFromIndex :: (Repository, Release) -> PackageIndex -> DebSource
+debSourceFromIndex (repo, release) index =
+    DebSource {sourceType = typ,
+               sourceUri = repoURI repo,
+               sourceDist = Right (dist, components)}
+    where
+      typ = case arch of (Binary _ _) -> Deb; Source -> DebSrc; All -> Deb
+      arch = packageIndexArch index
+      dist = releaseName $ release
+      components = releaseComponents $ release
diff --git a/testdata/split1-expected/Debian/Repo/Slice.hs b/testdata/split1-expected/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Slice.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE PackageImports, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , verifySourceLine
+    , verifySourcesList
+    , repoSources
+    , parseNamedSliceList
+    , parseNamedSliceList'
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName)
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.SourcesList (parseSourceLine, parseSourcesList)
+import Debian.Repo.Types (EnvPath(..), EnvRoot(..))
+import Debian.Repo.Types.Repo (repoKey, RepoKey(..))
+import Debian.Repo.Types.Repository (prepareRepository)
+import Debian.Repo.Types.Slice (NamedSliceList(..), Slice(..), SliceList(..))
+import Debian.Sources (DebSource(..), SliceName(SliceName), SourceType(..))
+import Debian.URI (dirFromURI, fileFromURI, toURI')
+import Network.URI (URI(uriScheme, uriPath))
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+-- |Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadApt m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+parseNamedSliceList :: MonadApt m => (String, String) -> m (Maybe NamedSliceList)
+parseNamedSliceList (name, text) =
+    (verifySourcesList Nothing . parseSourcesList) text >>=
+    \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+-- |Create ReleaseCache info from an entry in the config file, which
+-- includes a dist name and the lines of the sources.list file.
+-- This also creates the basic 
+parseNamedSliceList' :: MonadApt m => (String, String) -> m NamedSliceList
+parseNamedSliceList' (name, text) =
+    do sources <- (verifySourcesList Nothing . parseSourcesList) text
+       return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+verifySourcesList :: MonadApt m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifySourceLine :: MonadApt m => Maybe EnvRoot -> String -> m Slice
+verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str)
+
+verifyDebSource :: MonadApt m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    repo >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      repo =
+          case uriScheme (sourceUri line) of
+            "file:" -> prepareRepository (Local (EnvPath chroot' (uriPath (sourceUri line))))
+            _ -> prepareRepository (Remote (toURI' (sourceUri line)))
+      chroot' = fromMaybe (EnvRoot "") chroot
diff --git a/testdata/split1-expected/Debian/Repo/SourcesList.hs b/testdata/split1-expected/Debian/Repo/SourcesList.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/SourcesList.hs
@@ -0,0 +1,142 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+--- | A DebSource represents a release of a remote repository and a
+--- method for accessing that repository.
+module Debian.Repo.SourcesList
+    (parseSourceLine,   -- String -> DebSource
+     parseSourceLine',  -- String -> Maybe DebSource
+     parseSourcesList,  -- String -> [DebSource]
+     quoteWords -- String -> [String]
+    )
+    where
+
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Sources (DebSource(..), SourceType(..))
+import Network.URI (parseURI, unEscapeString)
+
+{-
+
+deb uri distribution [component1] [componenent2] [...]
+
+The URI for the deb type must specify the base of the Debian
+distribution, from which APT will find the information it needs.
+
+distribution can specify an exact path, in which case the components
+must be omitted and distribution must end with a slash (/).
+
+If distribution does not specify an exact path, at least one component
+must be present.
+
+Distribution may also contain a variable, $(ARCH), which expands to
+the Debian architecture (i386, m68k, powerpc, ...)  used on the
+system.
+
+The rest of the line can be marked as a comment by using a #.
+
+Additional Notes:
+
+ + Lines can begin with leading white space.
+
+ + If the dist ends with slash (/), then it must be an absolute path
+   and it is an error to specify components after it.
+
+-}
+
+-- |quoteWords - similar to words, but with special handling of
+-- double-quotes and brackets.
+--
+-- The handling double quotes and [] is supposed to match:
+-- apt-0.6.44.2\/apt-pkg\/contrib\/strutl.cc:ParseQuoteWord()
+--
+-- The behaviour can be defined as:
+--
+--  Break the string into space seperated words ignoring spaces that
+--  appear between \"\" or []. Strip trailing and leading white space
+--  around words. Strip out double quotes, but leave the square
+--  brackets intact.
+quoteWords :: String -> [String]
+quoteWords [] = []
+quoteWords s = quoteWords' (dropWhile (==' ') s)
+    where
+      quoteWords' :: String -> [String]
+      quoteWords' [] = []
+      quoteWords' str =
+          case break (flip elem " [\"") str of
+            ([],[]) -> []
+            (w, []) -> [w]
+            (w, (' ':rest)) -> w : (quoteWords' (dropWhile (==' ') rest))
+            (w, ('"':rest)) ->
+                case break (== '"') rest of
+                  (w',('"':rest)) ->
+                      case quoteWords' rest of
+                        [] ->  [w ++ w']
+                        (w'':ws) -> ((w ++ w' ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing \" in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            (w, ('[':rest)) ->
+                case break (== ']') rest of
+                  (w',(']':rest)) ->
+                      case quoteWords' rest of
+                        []       -> [w ++ "[" ++ w' ++ "]"]
+                        (w'':ws) -> ((w ++ "[" ++ w' ++ "]" ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing ] in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            _ -> error ("the impossible happened in SourcesList.quoteWords")
+
+stripLine :: String -> String
+stripLine = takeWhile (/= '#') . dropWhile (== ' ')
+
+sourceLines :: String -> [String]
+sourceLines = filter (not . null) . map stripLine . lines
+
+-- |parseSourceLine -- parses a source line
+-- the argument must be a non-empty, valid source line with comments stripped
+-- see: 'sourceLines'
+parseSourceLine :: String -> DebSource
+parseSourceLine str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Deb
+                          "deb-src" -> DebSrc
+                          o -> error ("parseSourceLine: invalid type " ++ o ++ " in line:\n" ++ str)
+              theUri = case parseURI theUriStr of
+                         Nothing -> error ("parseSourceLine: invalid uri " ++ theUriStr ++ " in the line:\n" ++ str)
+                         Just u -> u
+              theDist = unEscapeString theDistStr
+          in
+            case last theDist of
+              '/' -> if null sections
+                      then DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              _ -> if null sections
+                    then error ("parseSourceLine: Dist is not an exact path, so at least one section is required on the line:\n" ++ str)
+                    else DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Right (parseReleaseName theDist, sections) }
+      _ -> error ("parseSourceLine: invalid line in sources.list:\n" ++ str)
+
+parseSourceLine' :: String -> Maybe DebSource
+parseSourceLine' str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Just Deb
+                          "deb-src" -> Just DebSrc
+                          _ -> Nothing
+              theUri = case parseURI theUriStr of
+                         Nothing -> Nothing
+                         Just u -> Just u
+              theDist = unEscapeString theDistStr
+          in
+            case (last theDist, theType, theUri) of
+              ('/', Just typ, Just uri) -> if null sections
+                      then Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              (_, Just typ, Just uri) -> if null sections
+                    then Nothing
+                    else Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Right ((parseReleaseName theDist), sections) }
+              _ -> Nothing
+      _ -> Nothing
+
+parseSourcesList :: String -> [DebSource]
+parseSourcesList = map parseSourceLine . sourceLines
diff --git a/testdata/split1-expected/Debian/Repo/Sync.hs b/testdata/split1-expected/Debian/Repo/Sync.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Sync.hs
@@ -0,0 +1,52 @@
+-- Comment above module head
+module Debian.Repo.Sync {-# WARNING "this is a warning" #-}
+    ( rsync
+    , foo
+    -- Comment after last export
+    ) where
+
+-- Comment before first import
+
+import Control.Monad.Trans (MonadIO)
+import qualified Data.ByteString as B (empty)
+import System.Exit (ExitCode)
+import System.FilePath (dropTrailingPathSeparator)
+-- Comment between two imporrts
+import System.Process (proc)
+import System.Process.Progress (keepResult, runProcessF)
+
+-- Comment before first decl
+rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode
+rsync extra source dest =
+    do result <- runProcessF (proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                                            [dropTrailingPathSeparator source ++ "/",
+                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult
+       case result of
+         [x] -> return x
+         _ -> error "Missing or multiple exit codes"
+
+-- Comment between two decls
+foo :: Int
+foo = 1
+
+{-
+handleExit 1 = "Syntax or usage error"
+handleExit 2 = "Protocol incompatibility"
+handleExit 3 = "Errors selecting input/output files, dirs"
+handleExit 4 = "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+handleExit 5 = "Error starting client-server protocol"
+handleExit 6 = "Daemon unable to append to log-file"
+handleExit 10 = "Error in socket I/O"
+handleExit 11 = "Error in file I/O"
+handleExit 12 = "Error in rsync protocol data stream"
+handleExit 13 = "Errors with program diagnostics"
+handleExit 14 = "Error in IPC code"
+handleExit 20 = "Received SIGUSR1 or SIGINT"
+handleExit 21 = "Some error returned by waitpid()"
+handleExit 22 = "Error allocating core memory buffers"
+handleExit 23 = "Partial transfer due to error"
+handleExit 24 = "Partial transfer due to vanished source files"
+handleExit 25 = "The --max-delete limit stopped deletions"
+handleExit 30 = "Timeout in data send/receive"
+handleExit 35 = "Timeout waiting for daemon connection"
+-}
diff --git a/testdata/split1-expected/Debian/Repo/Types.hs b/testdata/split1-expected/Debian/Repo/Types.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types.hs
@@ -0,0 +1,11 @@
+module Debian.Repo.Types
+    ( module Debian.Repo.Types.AptImage
+    , module Debian.Repo.Types.EnvPath
+    , module Debian.Repo.Types.PackageIndex
+    , module Debian.Repo.Types.Release
+    ) where
+
+import Debian.Repo.Types.AptImage
+import Debian.Repo.Types.EnvPath
+import Debian.Repo.Types.PackageIndex
+import Debian.Repo.Types.Release
diff --git a/testdata/split1-expected/Debian/Repo/Types/AptBuildCache.hs b/testdata/split1-expected/Debian/Repo/Types/AptBuildCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/AptBuildCache.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptBuildCache
+    ( AptBuildCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache)
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+-- | An apt cache with extra sources.list lines for a local upload repository.
+class AptCache t => AptBuildCache t where
+    -- | The sources.list
+    aptSliceList :: t -> SliceList
diff --git a/testdata/split1-expected/Debian/Repo/Types/AptCache.hs b/testdata/split1-expected/Debian/Repo/Types/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/AptCache.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptCache
+    ( AptCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+class (Ord t, Eq t, Show t) => AptCache t where
+    globalCacheDir :: t -> FilePath
+    -- | The directory you might chroot to.
+    rootDir :: t -> EnvRoot
+    -- | The sources.list without the local repository
+    aptBaseSliceList :: t -> SliceList
+    -- | The build architecture
+    aptArch :: t -> Arch
+    -- | Return the all source packages in this AptCache.
+    aptSourcePackages :: t -> [SourcePackage]
+    -- | Return the all binary packages for the architecture of this AptCache.
+    aptBinaryPackages :: t -> [BinaryPackage]
+    -- | Name of release
+    aptReleaseName :: t -> ReleaseName
diff --git a/testdata/split1-expected/Debian/Repo/Types/AptImage.hs b/testdata/split1-expected/Debian/Repo/Types/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/AptImage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptImage
+    ( AptImage(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import qualified Debian.Repo.Types.Slice as T (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+data AptImage =
+    AptImage { aptGlobalCacheDir :: FilePath
+             , aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSliceList :: T.SliceList
+             , aptImageReleaseName :: ReleaseName
+             , aptImageSourcePackages :: [SourcePackage]
+             , aptImageBinaryPackages :: [BinaryPackage]
+             }
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (aptImageReleaseName apt)
+
+instance AptCache AptImage where
+    globalCacheDir = aptGlobalCacheDir
+    rootDir = aptImageRoot
+    aptArch = aptImageArch
+    aptBaseSliceList = aptImageSliceList
+    aptSourcePackages = aptImageSourcePackages
+    aptBinaryPackages = aptImageBinaryPackages
+    aptReleaseName = aptImageReleaseName
+
+instance Ord AptImage where
+    compare a b = compare (aptImageReleaseName a) (aptImageReleaseName b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
diff --git a/testdata/split1-expected/Debian/Repo/Types/EnvPath.hs b/testdata/split1-expected/Debian/Repo/Types/EnvPath.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/EnvPath.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.EnvPath
+    ( EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
diff --git a/testdata/split1-expected/Debian/Repo/Types/PackageIndex.hs b/testdata/split1-expected/Debian/Repo/Types/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/PackageIndex.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.PackageIndex
+    ( PackageIndex(..)
+    , PackageIndexLocal
+    , PackageID(packageVersion)
+    , prettyPackageID
+    , BinaryPackage(..)
+    , binaryPackageName
+    , prettyBinaryPackage
+    , makeBinaryPackageID
+    , SourcePackage(..)
+    , sourcePackageName
+    , makeSourcePackageID
+    , SourceControl(..)
+    , SourceFileSpec(..)
+    , PackageIDLocal
+    , BinaryPackageLocal
+    , SourcePackageLocal
+    , PackageVersion(..)
+    , PkgVersion(..)
+    , prettyPkgVersion
+    ) where
+
+import Data.Text (Text, map)
+import Debian.Arch (Arch(..))
+import qualified Debian.Control.Text as T (Paragraph)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName, Relations)
+import Debian.Release (Section(..))
+import Debian.Repo.Orphans ({- instances -})
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)
+
+class (Eq a, Ord a) => PackageVersion a where
+    pkgName :: a -> BinPkgName
+    pkgVersion :: a -> DebianVersion
+
+-- |This is an old type which is still used to interface with the
+-- Debian.Relation module.
+data PkgVersion = PkgVersion { getName :: BinPkgName
+                             , getVersion :: DebianVersion
+                             } deriving (Eq, Ord, Show)
+
+instance PackageVersion PkgVersion where
+    pkgName = getName
+    pkgVersion = getVersion
+
+prettyPkgVersion :: PkgVersion -> Doc
+prettyPkgVersion v = pretty (getName v) <> text "=" <> prettyDebianVersion (getVersion v)
+
+---------------- PACKAGES AND PACKAGE INDEXES -------------
+
+-- |The PackageIndex type represents a file containing control
+-- information about debian packages, either source or binary.
+-- Though the control information for a binary package does not
+-- specify an architecture, the architecture here is that of
+-- the environment where the package information is cached.
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+type PackageIndexLocal = PackageIndex
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage p = pretty (pkgName p) <> text "-" <> prettyDebianVersion (pkgVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
+
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+
+-- | The 'PackageID' type fully identifies a package by name, version,
+-- and a 'PackageIndex' which identifies the package's release,
+-- component and architecture.
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+binaryPackageName :: BinaryPackage -> BinPkgName
+binaryPackageName = packageName . packageID
+
+sourcePackageName :: SourcePackage -> SrcPkgName
+sourcePackageName = packageName . sourcePackageID
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = pretty (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      }
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+type PackageIDLocal n = PackageID n
+type BinaryPackageLocal = BinaryPackage
+type SourcePackageLocal = SourcePackage
diff --git a/testdata/split1-expected/Debian/Repo/Types/Release.hs b/testdata/split1-expected/Debian/Repo/Types/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/Release.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , makeReleaseInfo
+    , parseComponents
+    , parseArchitectures
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure))
+import Data.Text (Text, unpack)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (fieldValue, Paragraph)
+import Debian.Release (parseSection', ReleaseName(..), Section(..))
+import Text.Regex (mkRegex, splitRegex)
+import qualified Tmp.File as F (File(..))
+
+-- FIXME: The lists here should be sets so that == and compare work properly.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: [Arch]
+                       , releaseComponents :: [Section]
+                       } deriving (Eq, Ord, Read, Show)
+
+makeReleaseInfo :: F.File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(F.File {F.text = Failure msgs}) _name _aliases =
+    error $ "Failure reading " ++ show (F.path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(F.File {F.text = Success info}) name aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = name
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (F.path file)
+
+parseArchitectures :: Text -> [Arch]
+parseArchitectures archList =
+    map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
diff --git a/testdata/split1-expected/Debian/Repo/Types/Repo.hs b/testdata/split1-expected/Debian/Repo/Types/Repo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/Repo.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repo
+    ( Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    ) where
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Text (unpack)
+import Debian.Repo.Types.EnvPath (EnvPath(..))
+import Debian.Repo.Types.Release (Release)
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
diff --git a/testdata/split1-expected/Debian/Repo/Types/Repository.hs b/testdata/split1-expected/Debian/Repo/Types/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/Repository.hs
@@ -0,0 +1,428 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repository
+    ( Repository
+    , LocalRepository, repoRoot, repoLayout, repoReleaseInfoLocal
+    , RemoteRepository
+    , Layout(..)
+    , MonadRepoCache(getRepoCache, putRepoCache)
+    , loadRepoCache
+    , saveRepoCache
+    , fromLocalRepository
+    , readLocalRepo
+    , prepareLocalRepository
+    , copyLocalRepo -- repoCD
+    , prepareRepository
+    , setRepositoryCompatibility
+    , flushLocalRepository
+    , poolDir'
+    -- , sliceReleaseNames
+    , repoReleaseNames
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure), maybeRead)
+import Control.Exception (ErrorCall(..), SomeException, toException)
+import Control.Monad (filterM, unless, when)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IO (catch)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.List (groupBy, intercalate, isPrefixOf, partition, sort)
+import Data.Map as Map (empty, fromList, insert, insertWith, lookup, Map, toList, union)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (Text, unpack)
+import Debian.Changes (ChangedFileSpec(changedFileSection), ChangesFile(changeInfo))
+import qualified Debian.Control.Text as T (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph, Paragraph')
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section, sectionName', SubSection(section))
+import Debian.Repo.Monads.Top (MonadTop, sub)
+import Debian.Repo.Sync (rsync)
+import Debian.Repo.Types.EnvPath (EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Types.Release (makeReleaseInfo, Release(releaseName))
+import Debian.Repo.Types.Repo (compatibilityFile, libraryCompatibilityLevel, Repo(..), RepoKey(..))
+import Debian.URI (dirFromURI, fileFromURI, fromURI', toURI', URI(uriScheme, uriPath), URI', uriToString')
+import Debian.UTF8 as Deb (decode)
+import Extra.Files (maybeWriteFile)
+import Extra.List (partitionM)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink, setFileMode)
+import System.Process.Progress (qPutStr, qPutStrLn, quieter)
+import System.Unix.Directory (removeRecursiveSafely)
+import qualified Text.Format as F (Pretty(..))
+import Text.PrettyPrint.ANSI.Leijen (text)
+import Text.Regex (matchRegex, mkRegex)
+import qualified Tmp.File as F (File(..), readFile, Source(RemotePath))
+
+--------------------- REPOSITORY -----------------------
+
+-- | The Repository type reprents any instance of the Repo class, so
+-- it might be local or remote.
+--data Repository = forall a. (Repo a) => Repository a
+data Repository
+    = LocalRepo LocalRepository
+    | RemoteRepo RemoteRepository
+    deriving (Show, Read)
+
+instance Ord Repository where
+    compare a b = compare (repoKey a) (repoKey b)
+
+instance Eq Repository where
+    a == b = compare a b == EQ
+
+-- | URI has a bogus show function, which we are using here.
+instance F.Pretty URI' where
+    pretty = text . show . fromURI'
+
+instance F.Pretty Repository where
+    pretty (LocalRepo r) = text $ outsidePath (repoRoot r)
+    pretty (RemoteRepo r) = F.pretty r
+
+instance Repo Repository where
+    repoKey (LocalRepo (LocalRepository path _ _)) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoKey (RemoteRepo (RemoteRepository uri _)) = Remote uri
+    repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
+    repoReleaseInfo (RemoteRepo (RemoteRepository _ info)) = info
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot_ :: EnvPath
+      , repoLayout_ :: (Maybe Layout)
+      , repoReleaseInfoLocal_ :: [Release]
+      } deriving (Read, Show)
+
+instance Ord LocalRepository where
+    compare (LocalRepository a _ _) (LocalRepository b _ _) = compare a b
+
+instance Eq LocalRepository where
+    a == b = compare a b == EQ
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show)
+
+instance Ord RemoteRepository where
+    compare (RemoteRepository a _) (RemoteRepository b _) = compare a b
+
+instance Eq RemoteRepository where
+    a == b = compare a b == EQ
+
+instance F.Pretty RemoteRepository where
+    pretty (RemoteRepository s _) = F.pretty s
+
+repoRoot :: LocalRepository -> EnvPath
+repoRoot = repoRoot_
+
+repoLayout :: LocalRepository -> Maybe Layout
+repoLayout = repoLayout_
+
+repoReleaseInfoLocal :: LocalRepository -> [Release]
+repoReleaseInfoLocal = repoReleaseInfoLocal_
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+class MonadIO m => MonadRepoCache m where
+    getRepoCache :: m (Map RepoKey Repository)
+    putRepoCache :: Map RepoKey Repository -> m ()
+
+modifyRepoCache :: MonadRepoCache m => (Map RepoKey Repository -> Map RepoKey Repository) -> m ()
+modifyRepoCache f = do
+    s <- getRepoCache
+    putRepoCache (f s)
+
+fromLocalRepository :: LocalRepository -> Repository
+fromLocalRepository = LocalRepo
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: (MonadRepoCache m, MonadTop m) => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty)) >>= putRepoCache
+    where
+      loadRepoCache' :: FilePath -> IO (Map RepoKey Repository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: (MonadIO m, MonadTop m, MonadRepoCache m) => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- getRepoCache
+             repoCache <- liftIO $ loadCache path
+             let merged = show . Map.toList $ Map.union live repoCache
+             liftIO (F.removeLink path `IO.catch` (\e -> unless (isDoesNotExistError e) (ioError e))) >> liftIO (writeFile path merged)
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map RepoKey Repository)
+            loadCache path =
+                readFile path `IO.catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+readLocalRepo :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+readLocalRepo root layout =
+    do state <- getRepoCache
+       case Map.lookup (Local root) state of
+         Just (RemoteRepo (RemoteRepository _ _)) -> error "readLocalRepo: internal error" -- somehow a remote repo got associated with a Local RepoKey
+         Just (LocalRepo repo) -> return repo
+         Nothing ->
+             do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+                (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+                linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+                let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+                let distGroups = groupBy fstEq . sort $ aliasPairs
+                let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+                releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+                qPutStrLn ("LocalRepository releaseInfo " ++ show root ++ ": " ++ show releaseInfo)
+                let repo = LocalRepository { repoRoot_ = root
+                                           , repoLayout_ = layout
+                                           , repoReleaseInfoLocal_ = releaseInfo }
+                putRepoCache (insertWith (\ _ x -> x) (Local root) (LocalRepo repo) state)
+                return repo
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist ++ "/Release"
+      distDir = outsidePath root ++ "/dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path dist aliases =
+    liftIO (F.readFile path) >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> F.File Text -> Release
+parseRelease name aliases file =
+    case F.text file of
+      Failure msgs -> error $ "Could not read " ++ show (F.path file) ++ ": " ++ show msgs
+      Success text ->
+          case T.parseControl (show (F.path file)) text of
+            Left msg -> error $ "Failure parsing " ++ show (F.path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (F.path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (F.File {F.path = F.path file, F.text = Success info}) name aliases
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository root layout =
+    do mapM_ (liftIO . initDir)
+                 [(".", 0o40755),
+                  ("dists", 0o40755),
+                  ("incoming", 0o41755),
+                  ("removed", 0o40750),
+                  ("reject", 0o40750)]
+       layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+                  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+       mapM_ (liftIO . initDir)
+                 (case layout' of
+                    Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+                    Just Flat -> []
+                    Nothing -> [])
+       readLocalRepo root layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+{-      notSymbolicLink root name =
+          getSymbolicLinkStatus (root ++ "/dists/" ++ name) >>= return . not . isSymbolicLink
+      hasReleaseFile root name =
+          doesFileExist (root ++ "/dists/" ++ name ++ "/Release") -}
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadRepoCache m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do qPutStrLn ("Syncing local repository from " ++ src ++ " -> " ++ dst)
+       liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       result <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         ExitSuccess ->
+             do let repo' = repo {repoRoot_ = dest}
+                modifyRepoCache (Map.insert (Local dest) (LocalRepo repo'))
+                return repo'
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path text
+    where text = show libraryCompatibilityLevel ++ "\n"
+          path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepoCache m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       prepareLocalRepository (repoRoot r) (repoLayout r)
+
+prepareRepository :: MonadRepoCache m => RepoKey -> m Repository
+prepareRepository key =
+    case key of
+      Local path -> prepareLocalRepository path Nothing >>= return . LocalRepo
+      Remote uri -> prepareRepository' (fromURI' uri)
+
+-- | Prepare a repository, which may be remote or local depending on
+-- the URI.
+prepareRepository' :: MonadRepoCache m => URI -> m Repository
+prepareRepository' uri =
+    do state <- getRepoCache
+       repo <- maybe newRepo return (Map.lookup (Remote (toURI' uri)) state)
+       putRepoCache (Map.insert (Remote (toURI' uri)) repo state)
+       return repo
+    where
+      newRepo =
+             case uriScheme uri of
+               "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= return . LocalRepo
+               -- FIXME: We only want to verifyRepository on demand.
+               _ -> verifyRepository (toURI' uri)
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+{-# NOINLINE verifyRepository #-}
+verifyRepository :: MonadRepoCache m => URI' -> m Repository
+verifyRepository uri =
+    do state <- getRepoCache
+       case Map.lookup (Remote uri) state of
+         Just repo -> return repo
+         Nothing ->
+             do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+                let repo = RemoteRepo (RemoteRepository uri releaseInfo)
+                modifyRepoCache (Map.insert (Remote uri) repo)
+                return repo
+
+-- Nice code to do caching, but I figured out how to fix the old code.
+
+--instance Read URI where
+--    readsPrec _ s = [(fromJust (parseURI s), "")]
+--
+---- |Get the list of releases of a remote repository.
+--getReleaseInfo :: FilePath
+--               -> Bool     -- ^ If False don't look at existing cache
+--               -> URI
+--               -> IO [ReleaseInfo]
+--getReleaseInfo top tryCache uri =
+--    readCache >>= \ cache ->
+--    return (lookup uri cache) >>=
+--    maybe (updateCache cache) return
+--    where
+--      cachePath = top ++ "/repoCache"
+--      readCache :: IO [(URI, [ReleaseInfo])]
+--      readCache = if tryCache
+--                  then try (readFile cachePath >>= return . read) >>= return . either (\ (_ :: SomeException) -> []) id
+--                  else return []
+--      updateCache :: [(URI, [ReleaseInfo])] -> IO [ReleaseInfo]
+--      updateCache pairs = getReleaseInfoRemote uri >>= \ info ->
+--                          writeCache ((uri, info) : pairs) >> return info
+--      writeCache :: [(URI, [ReleaseInfo])] -> IO ()
+--      writeCache pairs = writeFile (show pairs) cachePath
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    quieter 2 (dirFromURI distsURI) >>=
+    quieter 2 . either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [F.File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (F.File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: F.File (T.Paragraph' Text) -> Maybe Text
+      getSuite (F.File {F.text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (F.File {F.text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (F.File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ F.File {F.path = F.RemotePath releaseURI, F.text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
+
+repoReleaseNames :: Repository -> [ReleaseName]
+repoReleaseNames (RemoteRepo (RemoteRepository _ rels)) = map releaseName rels
+repoReleaseNames (LocalRepo (LocalRepository _ _ rels)) = map releaseName rels
diff --git a/testdata/split1-expected/Debian/Repo/Types/Slice.hs b/testdata/split1-expected/Debian/Repo/Types/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Debian/Repo/Types/Slice.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Slice
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    ) where
+
+import qualified Debian.Repo.Types.Repo as R (RepoKey)
+import Debian.Sources (DebSource(..), SliceName(..), SourceType(..))
+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), vcat)
+
+data Slice = Slice {sliceRepoKey :: R.RepoKey, sliceSource :: DebSource} deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty SliceList where
+    pretty = vcat . map (pretty . sliceSource) . slices
+
+deriving instance Show SourceType
+deriving instance Show DebSource
diff --git a/testdata/split1-expected/Text/Format.hs b/testdata/split1-expected/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/split1-expected/Tmp/File.hs b/testdata/split1-expected/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split1-expected/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
diff --git a/testdata/split2-clean-expected/Split/Clean.hs b/testdata/split2-clean-expected/Split/Clean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/Clean.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Clean
+    ( -- Symbols that are exported but not declared go into ReExported
+      clean
+    ) where
+
+import Data.Char (isAlphaNum)
+import URL (ToURL(toURL), URLT)
+
+clean :: (ToURL url, Show (URLT url)) => url -> String
+clean = filter isAlphaNum . show . toURL
diff --git a/testdata/split2-clean-expected/Split/Foo.hs b/testdata/split2-clean-expected/Split/Foo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/Foo.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Foo
+    ( -- Symbols that are exported but not declared go into ReExported
+      foo
+    ) where
+
+
+
+-- Exported symbols.
+foo :: Int
+foo = 1
diff --git a/testdata/split2-clean-expected/Split/Instances.hs b/testdata/split2-clean-expected/Split/Instances.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/Instances.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Instances
+    ( -- Symbols that are exported but not declared go into ReExported
+      
+    ) where
+
+import Split.Internal.Bar (Bar(Bar))
+
+instance Show Bar where
+    show (Bar n) = "Bar " ++ show n
diff --git a/testdata/split2-clean-expected/Split/Internal/Bar.hs b/testdata/split2-clean-expected/Split/Internal/Bar.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/Internal/Bar.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Internal.Bar
+    ( -- Symbols that are exported but not declared go into ReExported
+      Bar(Bar)
+    ) where
+
+
+
+newtype Bar = Bar Int
diff --git a/testdata/split2-clean-expected/Split/Internal/Unexp.hs b/testdata/split2-clean-expected/Split/Internal/Unexp.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/Internal/Unexp.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Internal.Unexp
+    ( -- Symbols that are exported but not declared go into ReExported
+      unexp
+    ) where
+
+
+
+-- Unexported symbols go into the Internal module
+unexp :: Int
+unexp = 3
diff --git a/testdata/split2-clean-expected/Split/OtherSymbols.hs b/testdata/split2-clean-expected/Split/OtherSymbols.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/OtherSymbols.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.OtherSymbols
+    ( -- Symbols that are exported but not declared go into ReExported
+      (+-+)
+    ) where
+
+
+
+-- Symbols that can't be turned into module names go into OtherSymbols
+(+-+) :: Int
+(+-+) = 2
diff --git a/testdata/split2-clean-expected/Split/ReExported.hs b/testdata/split2-clean-expected/Split/ReExported.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/Split/ReExported.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.ReExported
+    ( -- Symbols that are exported but not declared go into ReExported
+      dropWhile
+    ) where
+
+
diff --git a/testdata/split2-clean-expected/URL.hs b/testdata/split2-clean-expected/URL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-clean-expected/URL.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module URL (ToURL(toURL, nullURL, errorURL), URLT) where
+
+class ToURL a where
+    type URLT a
+    toURL :: a -> URLT a
+    nullURL :: a
+    errorURL :: a -> URLT a
diff --git a/testdata/split2-expected/Split/Clean.hs b/testdata/split2-expected/Split/Clean.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/Clean.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Clean
+    ( -- Symbols that are exported but not declared go into ReExported
+      clean
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+clean :: (ToURL url, Show (URLT url)) => url -> String
+clean = filter isAlphaNum . show . toURL
diff --git a/testdata/split2-expected/Split/Foo.hs b/testdata/split2-expected/Split/Foo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/Foo.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Foo
+    ( -- Symbols that are exported but not declared go into ReExported
+      foo
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+-- Exported symbols.
+foo :: Int
+foo = 1
diff --git a/testdata/split2-expected/Split/Instances.hs b/testdata/split2-expected/Split/Instances.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/Instances.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Instances
+    ( -- Symbols that are exported but not declared go into ReExported
+      
+    ) where
+
+import Split.Internal.Bar (Bar(Bar))
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+instance Show Bar where
+    show (Bar n) = "Bar " ++ show n
diff --git a/testdata/split2-expected/Split/Internal/Bar.hs b/testdata/split2-expected/Split/Internal/Bar.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/Internal/Bar.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Internal.Bar
+    ( -- Symbols that are exported but not declared go into ReExported
+      Bar(Bar)
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+newtype Bar = Bar Int
diff --git a/testdata/split2-expected/Split/Internal/Unexp.hs b/testdata/split2-expected/Split/Internal/Unexp.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/Internal/Unexp.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.Internal.Unexp
+    ( -- Symbols that are exported but not declared go into ReExported
+      unexp
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+-- Unexported symbols go into the Internal module
+unexp :: Int
+unexp = 3
diff --git a/testdata/split2-expected/Split/OtherSymbols.hs b/testdata/split2-expected/Split/OtherSymbols.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/OtherSymbols.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.OtherSymbols
+    ( -- Symbols that are exported but not declared go into ReExported
+      (+-+)
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+-- Symbols that can't be turned into module names go into OtherSymbols
+(+-+) :: Int
+(+-+) = 2
diff --git a/testdata/split2-expected/Split/ReExported.hs b/testdata/split2-expected/Split/ReExported.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/Split/ReExported.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split.ReExported
+    ( -- Symbols that are exported but not declared go into ReExported
+      dropWhile
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
diff --git a/testdata/split2-expected/URL.hs b/testdata/split2-expected/URL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2-expected/URL.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module URL (ToURL(toURL, nullURL, errorURL), URLT) where
+
+class ToURL a where
+    type URLT a
+    toURL :: a -> URLT a
+    nullURL :: a
+    errorURL :: a -> URLT a
diff --git a/testdata/split2/Split.hs b/testdata/split2/Split.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2/Split.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleContexts #-}
+-- Split a module with a re-export
+module Split
+    ( -- Symbols that are exported but not declared go into ReExported
+      dropWhile
+    , foo
+    , (+-+)
+    , clean
+    ) where
+
+import Data.Char (isAlphaNum)
+import Data.List (dropWhile)
+import URL
+
+-- Exported symbols.
+foo :: Int
+foo = 1
+
+-- Symbols that can't be turned into module names go into OtherSymbols
+(+-+) :: Int
+(+-+) = 2
+
+-- Unexported symbols go into the Internal module
+unexp :: Int
+unexp = 3
+
+newtype Bar = Bar Int
+
+instance Show Bar where
+    show (Bar n) = "Bar " ++ show n
+
+clean :: (ToURL url, Show (URLT url)) => url -> String
+clean = filter isAlphaNum . show . toURL
diff --git a/testdata/split2/URL.hs b/testdata/split2/URL.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split2/URL.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module URL (ToURL(toURL, nullURL, errorURL), URLT) where
+
+class ToURL a where
+    type URLT a
+    toURL :: a -> URLT a
+    nullURL :: a
+    errorURL :: a -> URLT a
diff --git a/testdata/split4-expected/Split4/BinaryPackagesOfIndex.hs b/testdata/split4-expected/Split4/BinaryPackagesOfIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4-expected/Split4/BinaryPackagesOfIndex.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.BinaryPackagesOfIndex 
+    ( binaryPackagesOfIndex
+    ) where
+import Split4.GetPackages (getPackages)
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index 
diff --git a/testdata/split4-expected/Split4/GetPackages.hs b/testdata/split4-expected/Split4/GetPackages.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4-expected/Split4/GetPackages.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.GetPackages 
+    ( getPackages
+    ) where
+
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
diff --git a/testdata/split4-expected/Split4/SourcePackagesOfIndex.hs b/testdata/split4-expected/Split4/SourcePackagesOfIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4-expected/Split4/SourcePackagesOfIndex.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.SourcePackagesOfIndex 
+    ( sourcePackagesOfIndex
+    ) where
+import Split4.GetPackages (getPackages)
+-- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
diff --git a/testdata/split4/Split4.hs b/testdata/split4/Split4.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4/Split4.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4 where
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
diff --git a/testdata/split4b-expected/Split4/A.hs b/testdata/split4b-expected/Split4/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4b-expected/Split4/A.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.A 
+    ( getPackages
+    ) where
+
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
diff --git a/testdata/split4b-expected/Split4/B.hs b/testdata/split4b-expected/Split4/B.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4b-expected/Split4/B.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.B 
+    ( binaryPackagesOfIndex
+    , sourcePackagesOfIndex
+    ) where
+import Split4.A (getPackages)
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
diff --git a/testdata/split4c-expected/Split4.hs b/testdata/split4c-expected/Split4.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4c-expected/Split4.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4 
+    ( binaryPackagesOfIndex
+    , sourcePackagesOfIndex
+    ) where
+import Split4.A (getPackages)
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
diff --git a/testdata/split4c-expected/Split4/A.hs b/testdata/split4c-expected/Split4/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split4c-expected/Split4/A.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Split4.A 
+    ( getPackages
+    ) where
+
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
diff --git a/testdata/split5-expected/A.hs b/testdata/split5-expected/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/A.hs
@@ -0,0 +1,13 @@
+module A
+    ( module B.A
+    , module B.B
+    , module C
+    , module D
+    , module E
+    ) where
+
+import B.A (a)
+import B.B (b)
+import C (c, d)
+import D
+import E
diff --git a/testdata/split5-expected/B/A.hs b/testdata/split5-expected/B/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/B/A.hs
@@ -0,0 +1,6 @@
+module B.A
+    ( a
+    ) where
+
+
+a = 'a'
diff --git a/testdata/split5-expected/B/B.hs b/testdata/split5-expected/B/B.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/B/B.hs
@@ -0,0 +1,4 @@
+module B.B
+    ( b
+    ) where
+b = 'b'
diff --git a/testdata/split5-expected/C.hs b/testdata/split5-expected/C.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/C.hs
@@ -0,0 +1,8 @@
+module C
+    ( c
+    , d
+    ) where
+
+c = 'c'
+d = 'd'
+
diff --git a/testdata/split5-expected/D.hs b/testdata/split5-expected/D.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/D.hs
@@ -0,0 +1,8 @@
+module D
+    ( e
+    , f
+    ) where
+
+e = 'e'
+f = 'f'
+
diff --git a/testdata/split5-expected/E.hs b/testdata/split5-expected/E.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5-expected/E.hs
@@ -0,0 +1,8 @@
+module B
+    ( g
+    , h
+    ) where
+
+g = 'g'
+h = 'h'
+
diff --git a/testdata/split5/A.hs b/testdata/split5/A.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5/A.hs
@@ -0,0 +1,11 @@
+module A
+    ( module B
+    , module C
+    , module D
+    , module E
+    ) where
+
+import B (a, b)
+import C (c, d)
+import D
+import E
diff --git a/testdata/split5/B.hs b/testdata/split5/B.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5/B.hs
@@ -0,0 +1,8 @@
+module B
+    ( a
+    , b
+    ) where
+
+a = 'a'
+b = 'b'
+
diff --git a/testdata/split5/C.hs b/testdata/split5/C.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5/C.hs
@@ -0,0 +1,8 @@
+module C
+    ( c
+    , d
+    ) where
+
+c = 'c'
+d = 'd'
+
diff --git a/testdata/split5/D.hs b/testdata/split5/D.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5/D.hs
@@ -0,0 +1,8 @@
+module D
+    ( e
+    , f
+    ) where
+
+e = 'e'
+f = 'f'
+
diff --git a/testdata/split5/E.hs b/testdata/split5/E.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split5/E.hs
@@ -0,0 +1,8 @@
+module B
+    ( g
+    , h
+    ) where
+
+g = 'g'
+h = 'h'
+
diff --git a/testdata/split6-expected/Debian/Repo/AptCache.hs b/testdata/split6-expected/Debian/Repo/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/AptCache.hs
@@ -0,0 +1,392 @@
+{- This is a comment at the top -}   
+{-# LANGUAGE DeriveDataTypeable, PackageImports #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |An AptCache represents a local cache of a remote repository.  The
+-- cached information is usually downloaded by running "apt-get
+-- update", and appears in @\/var\/lib\/apt\/lists@.
+module Debian.Repo.AptCache {-# WARNING "this is a warning" #-}
+    ( SourcesChangedAction(..)
+    , aptSourcePackagesSorted
+    , sliceIndexes
+    , cacheDistDir
+    , distDir
+    , aptDir
+    , cacheRootDir
+    , cacheSourcesPath
+    , sourcesPath
+    , sourceDir
+    , aptCacheFiles
+    , aptCacheFilesOfSlice
+    , archFiles
+    , buildArchOfEnv
+    , buildArchOfRoot
+    , updateCacheSources
+    , sourcePackages
+    , binaryPackages
+    , runAptGet
+    , aptOpts
+    , getSourcePackagesBase
+    , getBinaryPackagesBase
+    , getSourcePackagesBuild
+    , getBinaryPackagesBuild
+    ) where
+
+import Control.DeepSeq (force, NFData)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Data (Data)
+import Data.List (intercalate, sortBy)
+import Data.Typeable (Typeable)
+import Debian.Arch (Arch(..), ArchCPU(..), ArchOS(..), prettyArch)
+import Debian.Relation (BinPkgName, PkgName, SrcPkgName(..))
+import Debian.Release (ReleaseName(relName), releaseName', sectionName')
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.Package (binaryPackagesOfCachedIndex, sourcePackagesOfCachedIndex)
+import Debian.Repo.Slice (binarySlices, sourceSlices, verifySourcesList)
+import Debian.Repo.SourcesList (parseSourcesList)
+import Debian.Repo.Types.AptBuildCache (AptBuildCache(aptSliceList))
+import Debian.Repo.Types.AptCache (AptCache(..), AptCache(aptArch, aptBaseSliceList, aptBinaryPackages, aptReleaseName, aptSourcePackages, globalCacheDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(..), EnvRoot(EnvRoot))
+import Debian.Repo.Types.PackageIndex (BinaryPackage(packageID), binaryPackageName, PackageID(packageVersion), PackageIndex(..), SourcePackage(sourcePackageID), sourcePackageName)
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (Repo(repoReleaseInfo), repoKey, RepoKey)
+import Debian.Repo.Types.Repository (MonadRepoCache, prepareRepository)
+import Debian.Repo.Types.Slice (Slice(..), SliceList(slices))
+import Debian.Sources (DebSource(..), SourceType(..))
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import Extra.Files (replaceFile)
+import Network.URI (escapeURIString, URI(uriAuthority, uriPath, uriScheme), URIAuth(uriPort, uriRegName, uriUserInfo))
+import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO (hGetLine, stdin)
+import System.Posix.Env (setEnv)
+import System.Process (readProcessWithExitCode, shell)
+import System.Process.Progress (ePutStr, ePutStrLn, qPutStrLn, runProcessF)
+import System.Unix.Chroot (useEnv)
+import System.Unix.Directory (removeRecursiveSafely)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+instance NFData ExitCode
+
+-- The following are path functions which can be used while
+-- constructing instances of AptCache.  Each is followed by a
+-- corresponding function that gives the same result when applied to
+-- an AptCache instance.
+
+-- | A directory which will hold all the cached files for this
+-- NamedSliceList.
+cacheDistDir :: FilePath -> ReleaseName -> FilePath
+cacheDistDir cacheDir release = cacheDir ++ "/dists/" ++ relName release
+
+cacheRootDir :: FilePath -> ReleaseName -> EnvRoot
+cacheRootDir cacheDir release = EnvRoot (cacheDistDir cacheDir release ++ "/aptEnv")
+
+distDir :: AptCache c => c -> FilePath
+distDir cache = cacheDistDir (globalCacheDir cache) (aptReleaseName cache)
+
+aptDir :: AptCache c => c -> SrcPkgName -> FilePath
+aptDir cache package = distDir cache ++ "/apt/" ++ unSrcPkgName package
+
+-- | The path where a text of the SliceList is stored.
+cacheSourcesPath :: FilePath -> ReleaseName -> FilePath
+cacheSourcesPath cacheDir release = cacheDistDir cacheDir release </> "sources"
+
+sourcesPath :: AptCache c => c -> FilePath
+sourcesPath cache = cacheSourcesPath (globalCacheDir cache) (aptReleaseName cache)
+
+-- Additional functions which can only be used on already constructed
+-- instances of AptCache.
+
+-- | A directory holding all files downloaded by apt-get source for a
+-- certain package
+sourceDir :: AptCache t => t -> String -> FilePath
+sourceDir c package = distDir c ++ "/apt/" ++ package
+
+-- |Return all the named source packages sorted by version
+aptSourcePackagesSorted :: AptCache t => t -> [SrcPkgName] -> [SourcePackage]
+aptSourcePackagesSorted os names =
+    sortBy cmp . filterNames names . aptSourcePackages $ os
+    where
+      filterNames names' packages =
+          filter (flip elem names' . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+-- |Return a list of the index files that contain the packages of a
+-- slice.
+sliceIndexes :: (MonadRepoCache m, AptCache a) => a -> Slice -> m [(RepoKey, Release, PackageIndex)]
+sliceIndexes cache slice =
+    prepareRepository (sliceRepoKey slice) >>= \ repo ->
+    case (sourceDist (sliceSource slice)) of
+      Left exact -> error $ "Can't handle exact path in sources.list: " ++ exact
+      Right (release, sections) -> return $ map (makeIndex repo release) sections
+    where
+      makeIndex repo release section =
+          (repoKey repo,
+           findReleaseInfo repo release,
+           PackageIndex { packageIndexComponent = section
+                        , packageIndexArch = case (sourceType (sliceSource slice)) of
+                                               DebSrc -> Source
+                                               Deb -> aptArch cache })
+      findReleaseInfo repo release =
+          case filter ((==) release . releaseName) (repoReleaseInfo repo) of
+            [x] -> x
+            [] -> error $ ("sliceIndexes: Invalid release name: " ++ releaseName' release ++
+                           "\n  You may need to remove ~/.autobuilder/repoCache." ++
+                           "\n  Available: " ++ (show . map releaseName . repoReleaseInfo $ repo)) ++
+                           "\n repoKey: " ++ show (repoKey repo) ++
+                           "\n repoReleaseInfo: " ++ show (repoReleaseInfo repo) ++
+                           "\n slice: " ++ show slice
+            xs -> error $ "Internal error 5 - multiple releases named " ++ releaseName' release ++ "\n" ++ show xs
+
+-- |Return the paths in the local cache of the index files of a slice list.
+aptCacheFiles :: AptCache a => a -> [DebSource] -> [FilePath]
+aptCacheFiles apt sources = concat . map (aptCacheFilesOfSlice apt) $ sources
+
+-- |Return the paths in the local cache of the index files of a single slice.
+aptCacheFilesOfSlice :: AptCache a => a -> DebSource -> [FilePath]
+aptCacheFilesOfSlice apt slice = archFiles (aptArch apt) slice
+
+-- |Return the list of files that apt-get update would write into
+-- \/var\/lib\/apt\/lists when it processed the given list of DebSource.
+archFiles :: Arch -> DebSource -> [FilePath]
+archFiles arch deb =
+    case (arch, deb) of
+      (Binary _ _, DebSource DebSrc _ _) ->
+          map (++ "_source_Sources") (archFiles' deb)
+      (Binary _os _cpu, DebSource Deb _ _) ->
+          map (++ ("_binary-" ++ show (prettyArch arch) ++ "_Packages")) (archFiles' deb)
+      (x, _) -> error $ "Invalid build architecture: " ++ show x
+
+archFiles' :: DebSource -> [FilePath]
+archFiles' deb =
+    let uri = sourceUri deb
+        distro = sourceDist deb in
+    let scheme = uriScheme uri
+        auth = uriAuthority uri
+        path = uriPath uri in
+    let userpass = maybe "" uriUserInfo auth
+        reg = maybeOfString $ maybe "" uriRegName auth
+        port = maybe "" uriPort auth in
+    let (user, pass) = break (== ':') userpass in
+    let user' = maybeOfString user
+        pass' = maybeOfString pass in
+    let uriText = prefix scheme user' pass' reg port path in
+    -- what about dist?
+    either (\ exact -> [(escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText ++ escape exact))])
+           (\ (dist, sections) ->
+                map (\ section ->
+                         (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+                          releaseName' dist ++ "_" ++ sectionName' section))
+                    sections)
+           distro
+    where
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid DebSource: " ++ show (pretty deb))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+
+buildArchOfEnv :: EnvRoot -> IO Arch
+buildArchOfEnv (EnvRoot root)  =
+    do setEnv "LOGNAME" "root" True -- This is required for dpkg-architecture to work in a build environment
+       a@(code1, out1, _err1) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- useEnv root (return . force) $ readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (ArchOS os) (ArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of build env at " ++ root ++ ": " ++ show (a, b)
+{-
+  (err, _) <- useEnv root forceList (readProcessChunks (shell cmd) L.empty) >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "export LOGNAME=root; dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+buildArchOfRoot :: IO Arch
+buildArchOfRoot =
+    do a@(code1, out1, _err1) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_OS"] ""
+       b@(code2, out2, _err2) <- readProcessWithExitCode "dpkg-architecture" ["-qDEB_BUILD_ARCH_CPU"] ""
+       case (code1, lines out1, code2, lines out2) of
+         (ExitSuccess, os : _, ExitSuccess, cpu : _) ->
+             return $ Binary (parseArchOS os) (parseArchCPU cpu)
+         _ -> error $ "Failure computing build architecture of /: " ++ show (a, b)
+    where
+      parseArchOS "any" = ArchOSAny
+      parseArchOS x = ArchOS x
+      parseArchCPU "any" = ArchCPUAny
+      parseArchCPU x = ArchCPU x
+{-
+    do (code, out, err, _) <- runProcess (shell cmd) L.empty >>= return . collectOutputs
+       case code of
+         (ExitSuccess : _) ->
+             case words (UTF8.toString (B.concat (L.toChunks out))) of
+               [] -> error $ "Invalid output from " ++ cmd
+               (arch : _) -> return (Binary arch)
+         _ -> error $ "Failure: " ++ cmd ++ " -> " ++ show code ++ "\n\nstdout:\n\n" ++ show out ++ "\n\nstderr:\n\n" ++ show err
+    where
+      cmd = "dpkg-architecture -qDEB_BUILD_ARCH"
+-}
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+wordsBy p s =
+    case (break p s) of
+      (s', []) -> [s']
+      (h, t) -> h : wordsBy p (drop 1 t)
+
+data SourcesChangedAction =
+    SourcesChangedError |
+    UpdateSources |
+    RemoveRelease
+    deriving (Eq, Show, Data, Typeable)
+
+-- |Change the sources.list of an AptCache object, subject to the
+-- value of sourcesChangedAction.
+updateCacheSources :: (MonadApt m, AptCache c) => SourcesChangedAction -> c -> m c
+updateCacheSources sourcesChangedAction distro =
+    -- (\ x -> qPutStrLn "Updating cache sources" >> quieter 2 x) $
+    qPutStrLn "Updating cache sources" >>
+    do
+      let baseSources = aptBaseSliceList distro
+      --let distro@(ReleaseCache _ dist _) = releaseFromConfig' top text
+      let dir = Debian.Repo.AptCache.distDir distro
+      distExists <- liftIO $ doesFileExist (Debian.Repo.AptCache.sourcesPath distro)
+      case distExists of
+        True ->
+            do
+              fileSources <- liftIO (readFile (Debian.Repo.AptCache.sourcesPath distro)) >>= verifySourcesList Nothing . parseSourcesList
+              case (fileSources == baseSources, sourcesChangedAction) of
+                (True, _) -> return ()
+                (False, SourcesChangedError) ->
+                    do
+                      ePutStrLn ("The sources.list in the existing '" ++ relName (aptReleaseName distro) ++
+                                 "' build environment doesn't match the parameters passed to the autobuilder" ++
+                                 ":\n\n" ++ Debian.Repo.AptCache.sourcesPath distro ++ ":\n\n" ++
+                                 show (pretty fileSources) ++
+                                 "\nRun-time parameters:\n\n" ++
+                                 show (pretty baseSources) ++ "\n" ++
+                                 "It is likely that the build environment in\n" ++
+                                 dir ++ " is invalid and should be rebuilt.")
+                      ePutStr $ "Remove it and continue (or exit)?  [y/n]: "
+                      result <- liftIO $ hGetLine stdin
+                      case result of
+                        ('y' : _) ->
+                            do
+                              liftIO $ removeRecursiveSafely dir
+                              liftIO $ createDirectoryIfMissing True dir
+                              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                        _ ->
+                            error ("Please remove " ++ dir ++ " and restart.")
+                (False, RemoveRelease) ->
+                    do
+                      ePutStrLn $ "Removing suspect environment: " ++ dir
+                      liftIO $ removeRecursiveSafely dir
+                      liftIO $ createDirectoryIfMissing True dir
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+                (False, UpdateSources) ->
+                    do
+                      -- The sources.list has changed, but it should be
+                      -- safe to update it.
+                      ePutStrLn $ "Updating environment with new sources.list: " ++ dir
+                      liftIO $ removeFile (Debian.Repo.AptCache.sourcesPath distro)
+                      liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+        False ->
+            do
+              liftIO $ createDirectoryIfMissing True dir
+              liftIO $ replaceFile (Debian.Repo.AptCache.sourcesPath distro) (show (pretty baseSources))
+      return distro
+
+-- | Return a sorted list of available source packages, newest version first.
+sourcePackages :: AptCache a => a -> [SrcPkgName] -> [SourcePackage]
+sourcePackages os names =
+    sortBy cmp . filterNames . aptSourcePackages $ os
+    where
+      filterNames :: [SourcePackage] -> [SourcePackage]
+      filterNames packages =
+          filter (flip elem names . sourcePackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+binaryPackages :: AptCache a => a -> [BinPkgName] -> [BinaryPackage]
+binaryPackages os names =
+    sortBy cmp . filterNames . aptBinaryPackages $ os
+    where
+      filterNames :: [BinaryPackage] -> [BinaryPackage]
+      filterNames packages =
+          filter (flip elem names . binaryPackageName) packages
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . packageID $ p1
+            v2 = packageVersion . packageID $ p2
+
+getSourcePackagesBase :: (AptCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getSourcePackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [SourcePackage]
+getSourcePackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . sourceSlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> sourcePackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBase :: (AptCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBase os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptBaseSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+getBinaryPackagesBuild :: (AptBuildCache a, MonadApt m) => a -> m [BinaryPackage]
+getBinaryPackagesBuild os =
+    do indexes <- mapM (sliceIndexes os) (slices . binarySlices . aptSliceList $ os) >>= return . concat
+       mapM (\ (repo, rel, index) -> binaryPackagesOfCachedIndex os repo rel index) indexes >>= return . concat
+
+-- | Note that apt-get source works for binary or source package names.
+runAptGet :: (PkgName n, AptCache t) => t -> FilePath -> String -> [(n, Maybe DebianVersion)] -> IO ()
+runAptGet os dir command packages =
+    createDirectoryIfMissing True dir >> runProcessF (shell cmd) L.empty >> return ()
+    where
+      cmd = (intercalate " " ("cd" : dir : "&&" : "apt-get" : aptOpts os : command : map formatPackage packages))
+      formatPackage (name, Nothing) = show (pretty name)
+      formatPackage (name, Just version) = show (pretty name) ++ "=" ++ show (prettyDebianVersion version)
+
+aptOpts :: AptCache t => t -> String
+aptOpts os =
+    (" -o=Dir::State::status=" ++ root ++ "/var/lib/dpkg/status" ++
+     " -o=Dir::State::Lists=" ++ root ++ "/var/lib/apt/lists" ++
+     " -o=Dir::Cache::Archives=" ++ root ++ "/var/cache/apt/archives" ++
+     " -o=Dir::Etc::SourceList=" ++ root ++ "/etc/apt/sources.list" ++
+     " -o=Dir::Etc::SourceParts=" ++ root ++ "/etc/apt/sources.list.d")
+    where root = rootPath . rootDir $ os
diff --git a/testdata/split6-expected/Debian/Repo/AptImage.hs b/testdata/split6-expected/Debian/Repo/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/AptImage.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS -fno-warn-orphans #-}
+-- |The AptImage object represents a partial OS image which is capable
+-- of running apt-get, and thus obtaining repository info and source
+-- code packages.
+module Debian.Repo.AptImage
+    ( prepareAptEnv
+    , updateAptEnv
+    ) where
+
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Lazy as L (empty)
+import Data.Function (on)
+import Data.List (sortBy)
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.AptCache (aptOpts, buildArchOfRoot, cacheRootDir, getBinaryPackagesBase, getSourcePackagesBase, SourcesChangedAction, updateCacheSources)
+import Debian.Repo.Monads.Apt (insertAptImage, lookupAptImage, MonadApt(getApt, putApt))
+import Debian.Repo.Types.AptImage (AptImage(..))
+import Debian.Repo.Types.EnvPath (EnvRoot(..))
+import Debian.Repo.Types.PackageIndex (PackageID(packageVersion), SourcePackage(sourcePackageID))
+import Debian.Repo.Types.Slice (NamedSliceList(sliceList, sliceListName))
+import Debian.Sources (SliceName(sliceName))
+import Extra.Files (replaceFile, writeFileIfMissing)
+import System.Directory (createDirectoryIfMissing)
+import System.Process (shell)
+import System.Process.Progress (qPutStrLn, quieter, runProcessF)
+import Text.PrettyPrint.ANSI.Leijen (pretty)
+
+prepareAptEnv :: MonadApt m =>
+                 FilePath               -- Put environment in a subdirectory of this
+              -> SourcesChangedAction   -- What to do if environment already exists and sources.list is different
+              -> NamedSliceList         -- The sources.list
+              -> m AptImage             -- The resulting environment
+prepareAptEnv cacheDir sourcesChangedAction sources =
+    (\ x -> qPutStrLn ("Preparing apt-get environment for " ++ show (sliceName (sliceListName sources))) >> quieter 2 x) $
+    getApt >>= return . lookupAptImage (sliceListName sources) >>=
+    maybe (prepareAptEnv' cacheDir sourcesChangedAction sources) return
+
+-- |Create a skeletal enviroment sufficient to run apt-get.
+{-# NOINLINE prepareAptEnv' #-}
+prepareAptEnv' :: MonadApt m => FilePath -> SourcesChangedAction -> NamedSliceList -> m AptImage
+prepareAptEnv' cacheDir sourcesChangedAction sources =
+    do let root = rootPath (cacheRootDir cacheDir (ReleaseName (sliceName (sliceListName sources))))
+       --vPutStrLn 2 $ "prepareAptEnv " ++ sliceName (sliceListName sources)
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/apt/lists/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/cache/apt/archives/partial")
+       liftIO $ createDirectoryIfMissing True (root ++ "/var/lib/dpkg")
+       liftIO $ createDirectoryIfMissing True (root ++ "/etc/apt")
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/status") ""
+       liftIO $ writeFileIfMissing True (root ++ "/var/lib/dpkg/diversions") ""
+       -- We need to create the local pool before updating so the
+       -- sources.list will be valid.
+       let sourceListText = show (pretty (sliceList sources))
+       -- ePut ("writeFile " ++ (root ++ "/etc/apt/sources.list") ++ "\n" ++ sourceListText)
+       liftIO $ replaceFile (root ++ "/etc/apt/sources.list") sourceListText
+       arch <- liftIO $ buildArchOfRoot
+       let os = AptImage { aptGlobalCacheDir = cacheDir
+                         , aptImageRoot = EnvRoot root
+                         , aptImageArch = arch
+                         , aptImageReleaseName = ReleaseName . sliceName . sliceListName $ sources
+                         , aptImageSliceList = sliceList sources
+                         , aptImageSourcePackages = []
+                         , aptImageBinaryPackages = [] }
+       os' <- updateCacheSources sourcesChangedAction os >>= updateAptEnv
+       getApt >>= putApt . insertAptImage (sliceListName sources) os'
+       return os'
+
+-- |Run apt-get update and then retrieve all the packages referenced
+-- by the sources.list.  The source packages are sorted so that
+-- packages with the same name are together with the newest first.
+{-# NOINLINE updateAptEnv #-}
+updateAptEnv :: MonadApt m => AptImage -> m AptImage
+updateAptEnv os =
+    liftIO (runProcessF (shell cmd) L.empty) >>
+    getSourcePackagesBase os >>= return . sortBy cmp >>= \ sourcePackages ->
+    getBinaryPackagesBase os >>= \ binaryPackages ->
+    return $ os { aptImageSourcePackages = sourcePackages
+                , aptImageBinaryPackages = binaryPackages }
+    where
+      cmd = "apt-get" ++ aptOpts os ++ " update"
+      -- Flip args to get newest version first
+      cmp = flip (compare `on` (packageVersion . sourcePackageID))
+{-
+      cmp p1 p2 =
+          compare v2 v1         -- Flip args to get newest first
+          where
+            v1 = packageVersion . sourcePackageID $ p1
+            v2 = packageVersion . sourcePackageID $ p2
+
+    putStrLn ("> " ++ cmd) >> system cmd >>= \ code ->
+    case code of
+      ExitSuccess -> return ()
+      ExitFailure n -> error $ cmd ++ " -> ExitFailure " ++ show n
+-}
diff --git a/testdata/split6-expected/Debian/Repo/Monads/Apt.hs b/testdata/split6-expected/Debian/Repo/Monads/Apt.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Monads/Apt.hs
@@ -0,0 +1,182 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module Debian.Repo.Monads.Apt
+    ( AptIOT
+    , AptIO
+    , AptState(AptState, aptImageMap, binaryPackageMap, releaseMap,
+         repoMap, sourcePackageMap)
+    , runAptIO
+    , runAptT
+    , tryAB
+    , tryJustAB
+    , initState
+    , setRepoMap
+    , getRepoMap
+    , lookupRepository
+    , insertRepository
+    , lookupAptImage
+    , insertAptImage
+    , lookupSourcePackages
+    , insertSourcePackages
+    , lookupBinaryPackages
+    , insertBinaryPackages
+    , readParagraphs
+    , findRelease
+    , putRelease
+    , MonadApt(getApt, putApt)
+    ) where
+
+import Control.Exception (try)
+import Control.Exception as E (Exception, tryJust)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Reader (ReaderT)
+import Control.Monad.State (get, mapStateT, MonadIO(..), MonadTrans(..), put, StateT(runStateT))
+import qualified Data.Map as Map (empty, insert, lookup, Map)
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(parseControlFromHandle), Paragraph)
+import Debian.Release (ReleaseName)
+import Debian.Repo.Types (AptImage, BinaryPackage, Release, SourcePackage)
+import Debian.Repo.Types.Repo (Repo(repoKey), RepoKey(..))
+import Debian.Repo.Types.Repository (MonadRepoCache(getRepoCache, putRepoCache), Repository)
+import Debian.Sources (SliceName)
+import qualified System.IO as IO (hClose, IOMode(ReadMode), openBinaryFile)
+import System.Posix.Files (deviceID, fileID, FileStatus, modificationTime)
+
+instance Ord FileStatus where
+    compare a b = compare (deviceID a, fileID a, modificationTime a) (deviceID b, fileID b, modificationTime b)
+
+instance Eq FileStatus where
+    a == b = compare a b == EQ
+
+-- | A new monad to support the IO requirements of the autobuilder.
+-- This uses the RWS monad.  The reader monad is used to store a flag
+-- indicating whether this is a dry run, and the style information
+-- associated with each output handle, including indentation, prefixing,
+-- and replacing the output with one dot per n output characters.
+-- The state monad stores information used to implement the current
+-- output style and includes state information about whether the console
+-- is at the beginning of a line, per-handle state information, and a
+-- cache of the repositories that have been verified.
+type AptIOT = StateT AptState
+type AptIO = AptIOT IO
+
+-- | This represents the state of the IO system.
+data AptState
+    = AptState
+      { repoMap :: Map.Map RepoKey Repository           -- ^ Map to look up known Repository objects
+      , releaseMap :: Map.Map (RepoKey, ReleaseName) Release -- ^ Map to look up known Release objects
+      , aptImageMap :: Map.Map SliceName AptImage       -- ^ Map to look up prepared AptImage objects
+      , sourcePackageMap :: Map.Map FilePath (FileStatus, [SourcePackage]) -- ^ The contents of a package index, and status of local cache of index file
+      , binaryPackageMap :: Map.Map FilePath (FileStatus, [BinaryPackage])
+      }
+
+-- |Perform an AptIO monad task in the IO monad.
+runAptIO :: AptIO a -> IO a
+runAptIO action = (runStateT action) initState >>= \ (a, _) -> return a
+
+runAptT :: Monad m => AptIOT m a -> m a
+runAptT action = (runStateT action) initState >>= return . fst
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryAB :: Exception e => AptIO a -> AptIO (Either e a)
+tryAB task =
+    do state <- get
+       mapStateT (try' state) task
+    where
+      try' state task' =
+          do result <- try task'
+             case result of
+               Left e -> return (Left e, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |Implementation of try for the AptIO monad.  If the task throws
+-- an exception the initial state will be restored.
+tryJustAB :: Exception e => (e -> Maybe b) -> AptIO a -> AptIO (Either b a)
+tryJustAB f task =
+    do state <- get
+       mapStateT (tryJust' state) task
+    where
+      tryJust' state task' =
+          do result <- tryJust f task'
+             case result of
+               Left b -> return (Left b, state)
+               Right (a, state') -> return (Right a, state')
+
+-- |The initial output state - at the beginning of the line, no special handle
+-- state information, no repositories in the repository map.
+initState :: AptState
+initState = AptState
+            { repoMap = Map.empty
+            , releaseMap = Map.empty
+            , aptImageMap = Map.empty
+            , sourcePackageMap = Map.empty
+            , binaryPackageMap = Map.empty
+            }
+
+setRepoMap :: Map.Map RepoKey Repository -> AptState -> AptState
+setRepoMap m state = state {repoMap = m}
+
+getRepoMap :: AptState -> Map.Map RepoKey Repository
+getRepoMap state = repoMap state
+
+lookupRepository :: RepoKey -> AptState -> Maybe Repository
+lookupRepository uri state = Map.lookup uri (repoMap state)
+
+insertRepository :: RepoKey -> Repository -> AptState -> AptState
+insertRepository uri repo state = state {repoMap = Map.insert uri repo (repoMap state)}
+
+lookupAptImage :: SliceName -> AptState -> Maybe AptImage
+lookupAptImage name state = Map.lookup  name (aptImageMap state)
+
+insertAptImage :: SliceName -> AptImage -> AptState -> AptState
+insertAptImage name image state = state {aptImageMap = Map.insert name image (aptImageMap state)}
+
+lookupSourcePackages :: FilePath -> AptState -> Maybe (FileStatus, [SourcePackage])
+lookupSourcePackages key state = Map.lookup key (sourcePackageMap state)
+
+insertSourcePackages :: FilePath -> (FileStatus, [SourcePackage]) -> AptState -> AptState
+insertSourcePackages key packages state = state {sourcePackageMap = Map.insert key packages (sourcePackageMap state)}
+
+lookupBinaryPackages :: FilePath -> AptState -> Maybe (FileStatus, [BinaryPackage])
+lookupBinaryPackages key state = Map.lookup key (binaryPackageMap state)
+
+insertBinaryPackages :: FilePath -> (FileStatus, [BinaryPackage]) -> AptState -> AptState
+insertBinaryPackages key packages state =
+    state {binaryPackageMap = Map.insert key packages (binaryPackageMap state)}
+
+readParagraphs :: FilePath -> IO [B.Paragraph]
+readParagraphs path =
+    do --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path)                 -- Debugging output
+       h <- IO.openBinaryFile path IO.ReadMode
+       B.Control paragraphs <- B.parseControlFromHandle path h >>= return . (either (error . show) id)
+       IO.hClose h
+       --IO.hPutStrLn IO.stderr ("OSImage.paragraphsFromFile " ++ path ++ " done.")     -- Debugging output
+       return paragraphs
+
+findRelease :: Repository -> ReleaseName -> AptState -> Maybe Release
+findRelease repo dist state =
+    Map.lookup (repoKey repo, dist) (releaseMap state)
+
+putRelease :: Repository -> ReleaseName -> Release -> AptState -> AptState
+putRelease repo dist release state =
+    state {releaseMap = Map.insert (repoKey repo, dist) release (releaseMap state)}
+
+class (MonadIO m, Functor m, MonadCatchIO m) => MonadApt m where
+    getApt :: m AptState
+    putApt :: AptState -> m ()
+
+instance (MonadIO m, Functor m, MonadCatchIO m) => MonadApt (AptIOT m) where
+    getApt = get
+    putApt = put
+
+instance MonadApt m => MonadApt (ReaderT s m) where
+    getApt = lift getApt
+    putApt = lift . putApt
+
+instance MonadApt m => MonadRepoCache m where
+    getRepoCache = getApt >>= return . repoMap
+    putRepoCache m = getApt >>= \ a -> putApt (a {repoMap = m})
diff --git a/testdata/split6-expected/Debian/Repo/Monads/Top.hs b/testdata/split6-expected/Debian/Repo/Monads/Top.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Monads/Top.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+{-# OPTIONS -Wall #-}
+module Debian.Repo.Monads.Top
+    ( TopT
+    , runTopT
+    , MonadTop(askTop)
+    , sub
+    ) where
+
+import Control.Monad.Reader (MonadReader(ask), ReaderT(runReaderT))
+import Control.Monad.State (StateT)
+import Control.Monad.Trans (lift)
+import System.FilePath ((</>), isRelative)
+
+newtype TopDir = TopDir {unTopDir :: FilePath}
+
+type TopT = ReaderT TopDir
+
+runTopT :: FilePath -> TopT m a -> m a
+runTopT path action = (runReaderT action) (TopDir path)
+
+class Monad m => MonadTop m where
+    askTop :: m FilePath
+
+instance Monad m => MonadTop (TopT m) where
+    askTop = ask >>= return . unTopDir
+
+sub :: MonadTop m => FilePath -> m FilePath
+sub path | isRelative path = askTop >>= \ top -> return $ top </> path
+sub path = fail ("sub - path argument must be relative: " ++ path)
+
+instance MonadTop m => MonadTop (StateT s m) where
+    askTop = lift askTop
diff --git a/testdata/split6-expected/Debian/Repo/Orphans.hs b/testdata/split6-expected/Debian/Repo/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Orphans.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Orphans where
+
+import Data.Text (Text)
+import qualified Debian.Control.Text as T
+
+deriving instance Show (T.Field' Text)
+deriving instance Ord (T.Field' Text)
+deriving instance Show T.Paragraph
+deriving instance Ord T.Paragraph
diff --git a/testdata/split6-expected/Debian/Repo/Package.hs b/testdata/split6-expected/Debian/Repo/Package.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Package.hs
@@ -0,0 +1,367 @@
+{-# LANGUAGE PackageImports, ScopedTypeVariables, TupleSections #-}
+{-# OPTIONS -fno-warn-name-shadowing #-}
+module Debian.Repo.Package
+    ( sourceFilePaths
+    , binaryPackageSourceVersion
+    , binarySourceVersion
+    , sourcePackageBinaryNames
+    , sourceBinaryNames
+    , toSourcePackage
+    , toBinaryPackage
+    , binaryPackageSourceID
+    , sourcePackageBinaryIDs
+    , sourcePackagesOfIndex
+    , sourcePackagesOfCachedIndex
+    , binaryPackagesOfIndex
+    , binaryPackagesOfCachedIndex
+    , getPackages
+    , putPackages
+    , releaseSourcePackages
+    , releaseBinaryPackages
+    , TH.FixityDirection(..)
+    , TH.Fixity(..)
+    ) where
+
+import Control.Exception as E (catch, ErrorCall(..), SomeException(..), try)
+import "mtl" Control.Monad.Trans (MonadIO(..))
+import qualified Data.ByteString.Lazy.Char8 as L (ByteString, fromChunks)
+import Data.Either (partitionEithers)
+import Data.List as List (intercalate, intersperse, map, partition)
+import Data.Maybe (catMaybes)
+import Data.Set as Set (fromList, map, Set, unions)
+import qualified Data.Text as T (concat, pack, Text, unpack)
+import Data.Text.Encoding (encodeUtf8)
+import Debian.Apt.Index (Compression(..), controlFromIndex)
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Control (ControlFunctions(asString, stripWS), formatParagraph, Paragraph')
+import qualified Debian.Control.Text as B (Control'(Control), ControlFunctions(lookupP), Field, Field'(Field), fieldValue, Paragraph)
+import Debian.Relation (BinPkgName(..))
+import qualified Debian.Relation.Text as B (ParseRelations(..), Relations)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Monads.Apt (insertBinaryPackages, insertSourcePackages, lookupBinaryPackages, lookupSourcePackages, MonadApt(getApt, putApt), readParagraphs)
+import Debian.Repo.PackageIndex (binaryIndexList, packageIndexPath, sourceIndexList)
+import Debian.Repo.Types.AptCache (AptCache(aptArch, rootDir))
+import Debian.Repo.Types.EnvPath (EnvRoot(rootPath), outsidePath)
+import Debian.Repo.Types.PackageIndex (BinaryPackage(..), BinaryPackageLocal, binaryPackageName, makeBinaryPackageID, makeSourcePackageID, PackageID(..), PackageIndex(..), PackageIndexLocal, SourceControl(..), SourceFileSpec(SourceFileSpec, sourceFileName), SourcePackage(..))
+import Debian.Repo.Types.Release (Release(releaseName))
+import Debian.Repo.Types.Repo (RepoKey, repoKeyURI)
+import Debian.Repo.Types.Repository (LocalRepository, MonadRepoCache, repoRoot)
+import Debian.URI (fileFromURIStrict)
+import Debian.Version (DebianVersion, parseDebianVersion)
+import qualified Debian.Version as V (buildDebianVersion, epoch, revision, version)
+import Extra.Files (writeAndZipFileWithBackup)
+import qualified Language.Haskell.TH.Syntax as TH (Fixity(..), FixityDirection(..))
+import Network.URI (escapeURIString, URI(..), URIAuth(..), uriToString)
+import System.FilePath ((</>), takeDirectory)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Posix (getFileStatus)
+import Text.Regex (matchRegex, mkRegex, splitRegex)
+
+class Constants p where
+    asBool :: p -> Maybe Bool
+    fromBool :: Bool -> p
+
+useConstants :: Constants p => p -> Maybe Bool
+useConstants x = asBool x
+
+uriToString' :: URI -> String
+uriToString' uri = uriToString id uri ""
+
+sourceFilePaths :: SourcePackage -> Set FilePath
+sourceFilePaths package =
+    Set.map ((sourceDirectory package) </>) . Set.map sourceFileName . Set.fromList . sourcePackageFiles $ package
+
+-- | Return the name and version number of the source package that
+-- generated this binary package.
+binaryPackageSourceVersion :: BinaryPackage -> Maybe (String, DebianVersion)
+binaryPackageSourceVersion package =
+    let binaryName = binaryPackageName package
+        binaryVersion = packageVersion . packageID $ package in
+    binarySourceVersion' binaryName binaryVersion (packageInfo package)
+
+-- |Return the name and version number of the source package that
+-- generated this binary package.
+-- see also: 'binaryPackageSourceVersion'
+binarySourceVersion :: Paragraph' T.Text -> Maybe ((BinPkgName, DebianVersion), (String, DebianVersion))
+binarySourceVersion paragraph =
+    let mBinaryName = fmap (BinPkgName . T.unpack) $ B.fieldValue "Package" paragraph
+        mBinaryVersion = fmap (parseDebianVersion . T.unpack) $ B.fieldValue "Version" paragraph
+    in
+      case (mBinaryName, mBinaryVersion) of
+        (Just binaryName, Just binaryVersion) ->
+            fmap ((,) (binaryName, binaryVersion)) $ binarySourceVersion' binaryName binaryVersion paragraph
+        _ -> Nothing
+
+binarySourceVersion' :: (ControlFunctions a) => BinPkgName -> DebianVersion -> Paragraph' a -> Maybe (String, DebianVersion)
+binarySourceVersion' binaryName binaryVersion paragraph =
+    case (B.fieldValue "Source" paragraph) of
+      Just source' ->
+          case matchRegex re (asString source') of
+            Just [name, _, ""] -> Just (name, binaryVersion)
+            Just [name, _, version] -> Just (name, copyEpoch binaryVersion (parseDebianVersion version))
+            _ -> error "internal error"
+      Nothing ->
+          Just (asString (unBinPkgName binaryName), binaryVersion)
+    where
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+      -- In the Packages file the version number in the Source: field has
+      -- the epoch number stripped off.  I don't know why - I should search
+      -- the Debian policy manual for this.  This puts it back on.
+      copyEpoch src dst = V.buildDebianVersion (V.epoch src) (V.version dst) (V.revision dst)
+
+sourcePackageBinaryNames :: SourcePackage -> [BinPkgName]
+sourcePackageBinaryNames package =
+    sourceBinaryNames (sourceParagraph package)
+
+sourceBinaryNames :: B.Paragraph -> [BinPkgName]
+sourceBinaryNames paragraph =
+    case B.fieldValue "Binary" paragraph of
+      Just names -> List.map BinPkgName (splitRegex (mkRegex "[ ,\t\n]+") (T.unpack names))
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ paragraph))
+
+toSourcePackage :: PackageIndex -> B.Paragraph -> SourcePackage
+toSourcePackage index package =
+    case (B.fieldValue "Directory" package,
+          B.fieldValue "Files" package,
+          B.fieldValue "Package" package,
+          maybe Nothing (Just . parseDebianVersion . T.unpack) (B.fieldValue "Version" package)) of
+      (Just directory, Just files, Just name, Just version) ->
+          case (parseSourcesFileList files, parseSourceParagraph package) of
+            (Right files', Right para) ->
+                SourcePackage
+                { sourcePackageID = makeSourcePackageID (T.unpack name) version
+                , sourceParagraph = package
+                , sourceControl = para
+                , sourceDirectory = T.unpack directory
+                , sourcePackageFiles = files' }
+            (Left messages, _) -> error $ "Invalid file list: " ++ show messages
+            (_, Left messages) -> error $ "Error in source paragraph\n package=" ++ show package ++ "\n  index=" ++ show index ++ "\n  messages:\n   " ++ intercalate "\n   " messages
+      x -> error $ "Missing info in source package control information in " ++ show index ++ " -> " ++ show x ++ " :\n" ++ T.unpack (formatParagraph package)
+    where
+      -- Parse the list of files in a paragraph of a Sources index.
+      parseSourcesFileList :: T.Text -> Either [String] [SourceFileSpec]
+      parseSourcesFileList text =
+          merge . catMaybes . List.map parseSourcesFiles . lines . T.unpack $ text
+      parseSourcesFiles line =
+          case words line of
+            [md5sum, size, name] -> Just (Right (SourceFileSpec md5sum (read size) name))
+            [] -> Nothing
+            _ -> Just (Left ("Invalid line in Files list: '" ++ show line ++ "'"))
+      merge x = case partition (either (const True) (const False)) x of
+                  (a, []) -> Left . catMaybes . List.map (either Just (const Nothing )) $ a
+                  (_, a) -> Right . catMaybes . List.map (either (const Nothing) Just) $ a
+
+parseSourceParagraph :: B.Paragraph -> Either [String] SourceControl
+parseSourceParagraph p =
+    -- Look up the required fields
+    case (B.fieldValue "Package" p,
+          B.fieldValue "Maintainer" p) of
+      (Just source', Just maintainer') ->
+          -- The optional fields can be parsed as pure values
+          Right (SourceControl
+                  { source = source'
+                  , maintainer = maintainer'
+                  , uploaders = maybe [] (: []) $ B.fieldValue "Uploaders" p
+                  , packageSection = fmap stripWS $ B.fieldValue "Section" p
+                  , packagePriority = fmap stripWS $ B.fieldValue "Priority" p
+                  , buildDepends = maybe [] (: []) $ B.fieldValue "Build-Depends" p
+                  , buildDependsIndep = maybe [] (: []) $ B.fieldValue "Build-Depends-Indep" p
+                  , buildConflicts = maybe [] (: []) $ B.fieldValue "Build-Conflicts" p
+                  , buildConflictsIndep = maybe [] (: []) $ B.fieldValue "Build-Conflicts-Indep" p
+                  , standardsVersion = fmap stripWS $ B.fieldValue "Standards-Version" p
+                  , homepage = fmap stripWS $ B.fieldValue "Homepage" p })
+      _x -> Left ["parseSourceParagraph - One or more required fields (Package, Maintainer, Standards-Version) missing: " ++ show p]
+
+toBinaryPackage :: Release -> PackageIndex -> B.Paragraph -> BinaryPackage
+toBinaryPackage release index p =
+    case (B.fieldValue "Package" p, B.fieldValue "Version" p) of
+      (Just name, Just version) ->
+          BinaryPackage
+          { packageID =
+                makeBinaryPackageID (T.unpack name) (parseDebianVersion (T.unpack version))
+          , packageInfo = p
+          , pDepends = tryParseRel $ B.lookupP "Depends" p
+          , pPreDepends = tryParseRel $ B.lookupP "Pre-Depends" p
+          , pConflicts = tryParseRel $ B.lookupP "Conflicts" p
+          , pReplaces =  tryParseRel $ B.lookupP "Replaces" p
+          , pProvides =  tryParseRel $ B.lookupP "Provides" p
+          }
+      _ -> error ("Invalid data in source index:\n " ++ packageIndexPath release index)
+
+tryParseRel :: Maybe B.Field -> B.Relations
+tryParseRel (Just (B.Field (_, relStr))) = either (error . show) id (B.parseRelations relStr)
+tryParseRel _ = []
+
+-- | Parse the /Source/ field of a binary package's control
+-- information, this may specify a version number for the source
+-- package if it differs from the version number of the binary
+-- package.
+binaryPackageSourceID :: PackageIndex -> BinaryPackage -> PackageID BinPkgName
+binaryPackageSourceID (PackageIndex component _) package =
+    case maybe Nothing (matchRegex re . T.unpack) (B.fieldValue "Source" (packageInfo package)) of
+      Just [name, _, ""] -> makeBinaryPackageID name (packageVersion pid)
+      Just [name, _, version] -> makeBinaryPackageID name (parseDebianVersion version)
+      _ -> error "Missing Source attribute in binary package info"
+    where
+      -- sourceIndex = PackageIndex component Source
+      pid = packageID package
+      re = mkRegex "^[ ]*([^ (]*)[ ]*(\\([ ]*([^ )]*)\\))?[ ]*$"
+
+sourcePackageBinaryIDs :: Arch -> PackageIndex -> SourcePackage -> [PackageID BinPkgName]
+sourcePackageBinaryIDs Source _ _ = error "invalid argument"
+sourcePackageBinaryIDs arch sourceIndex package =
+    case (B.fieldValue "Version" info, B.fieldValue "Binary" info) of
+      (Just version, Just names) -> List.map (binaryID (parseDebianVersion (T.unpack version))) $ splitRegex (mkRegex "[ ,]+") (T.unpack names)
+      _ -> error ("Source package info has no 'Binary' field:\n" ++ (T.unpack . formatParagraph $ info))
+    where
+      -- Note that this version number may be wrong - we need to
+      -- look at the Source field of the binary package info.
+      binaryID version name = makeBinaryPackageID name version
+      -- binaryIndex = sourceIndex { packageIndexArch = arch }
+      info = sourceParagraph package
+
+-- | Get the contents of a package index
+getPackages :: RepoKey -> Release -> PackageIndex -> IO (Either SomeException [BinaryPackage])
+getPackages repo release index =
+    fileFromURIStrict uri' >>= readControl . either (Left . SomeException) Right
+    where
+      readControl :: Either SomeException L.ByteString -> IO (Either SomeException [BinaryPackage])
+      readControl (Left e) = return (Left e)
+      readControl (Right s) =
+          try (case controlFromIndex Uncompressed (show uri') s of
+                 Left e -> return $ Left (SomeException (ErrorCall (show uri' ++ ": " ++ show e)))
+                 Right (B.Control control) -> return (Right $ List.map (toBinaryPackage release index) control)) >>=
+          return . either (\ (e :: SomeException) -> Left . SomeException . ErrorCall . ((show uri' ++ ":") ++) . show $ e) id
+      uri' = uri {uriPath = uriPath uri </> packageIndexPath release index}
+      uri = repoKeyURI repo
+      --toLazy s = L.fromChunks [s]
+      --showStream :: Either Exception L.ByteString -> IO (Either Exception L.ByteString)
+      --showStream x@(Left e) = hPutStrLn stderr (show uri' ++ " - exception: " ++ show e) >> return x
+      --showStream x@(Right s) = hPutStrLn stderr (show uri' ++ " - stream length: " ++ show (L.length s)) >> return x
+
+-- | Get the contents of a package index
+binaryPackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [BinaryPackage])
+binaryPackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> return (Right [])
+      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))
+
+-- | Get the contents of a package index
+sourcePackagesOfIndex :: MonadRepoCache m => RepoKey -> Release -> PackageIndex -> m (Either SomeException [SourcePackage])
+sourcePackagesOfIndex repo release index =
+    case packageIndexArch index of
+      Source -> liftIO (getPackages repo release index) >>= return . either Left (Right . List.map (toSourcePackage index . packageInfo))
+      _ -> return (Right [])
+
+-- FIXME: assuming the index is part of the cache
+sourcePackagesOfCachedIndex :: (AptCache a, MonadApt m) => a -> RepoKey -> Release -> PackageIndex -> m [SourcePackage]
+sourcePackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupSourcePackages path state
+       status <- liftIO $ getFileStatus path `E.catch` (\ (_ :: IOError) -> error $ "Sources.list seems out of sync.  If a new release has been created you probably need to remove " ++ takeDirectory (rootPath (rootDir cache)) ++ " and try again - sorry about that.")
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toSourcePackage index) paragraphs
+                 putApt (insertSourcePackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+indexCacheFile :: (AptCache a) => a -> RepoKey -> Release -> PackageIndex -> FilePath
+indexCacheFile apt repo release index =
+    case (aptArch apt, packageIndexArch index) of
+      (Binary _ _, Source) -> indexPrefix repo release index ++ "_source_Sources"
+      (Binary _ _, arch@(Binary _ _)) -> indexPrefix repo release index ++ "_binary-" ++ show (prettyArch arch) ++ "_Packages"
+      (x, _) -> error "Invalid build architecture: " ++ show x
+
+indexPrefix :: RepoKey -> Release -> PackageIndex -> FilePath
+indexPrefix repo release index =
+    (escapeURIString (/= '@') ("/var/lib/apt/lists/" ++ uriText +?+ "dists_") ++
+     releaseName' distro ++ "_" ++ (sectionName' $ section))
+    where
+      section = packageIndexComponent index
+      uri = repoKeyURI repo
+      distro = releaseName $ release
+      scheme = uriScheme uri
+      auth = uriAuthority uri
+      path = uriPath uri
+      userpass = maybe "" uriUserInfo auth
+      reg = maybeOfString $ maybe "" uriRegName auth
+      port = maybe "" uriPort auth
+      (user, pass) = break (== ':') userpass
+      user' = maybeOfString user
+      pass' = maybeOfString pass
+      uriText = prefix scheme user' pass' reg port path
+      -- If user is given and password is not, the user name is
+      -- added to the file name.  Otherwise it is not.  Really.
+      prefix "http:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "http:" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix "ftp:" _ _ (Just host) _ path =
+          host ++ escape path
+      prefix "file:" Nothing Nothing Nothing "" path =
+          escape path
+      prefix "ssh:" (Just user) Nothing (Just host) port path =
+          user ++ host ++ port ++ escape path
+      prefix "ssh" _ _ (Just host) port path =
+          host ++ port ++ escape path
+      prefix _ _ _ _ _ _ = error ("invalid repo URI: " ++ (uriToString' . repoKeyURI $ repo))
+      maybeOfString "" = Nothing
+      maybeOfString s = Just s
+      escape s = intercalate "_" (wordsBy (== '/') s)
+      wordsBy :: Eq a => (a -> Bool) -> [a] -> [[a]]
+      wordsBy p s =
+          case (break p s) of
+            (s, []) -> [s]
+            (h, t) -> h : wordsBy p (drop 1 t)
+
+(+?+) :: String -> String -> String
+(+?+) a ('_' : b) = a +?+ b
+(+?+) "" b = b
+(+?+) a b =
+    case last a of
+      '_' -> (init a) +?+ b
+      _ -> a ++ "_" ++ b
+
+-- FIXME: assuming the index is part of the cache
+binaryPackagesOfCachedIndex :: (MonadApt m, AptCache a) => a -> RepoKey -> Release -> PackageIndex -> m [BinaryPackage]
+binaryPackagesOfCachedIndex cache repo release index =
+    do state <- getApt
+       let cached = lookupBinaryPackages path state
+       status <- liftIO $ getFileStatus path
+       case cached of
+         Just (status', packages) | status == status' -> return packages
+         _ -> do paragraphs <- liftIO $ unsafeInterleaveIO (readParagraphs path)
+                 let packages = List.map (toBinaryPackage release index) paragraphs
+                 putApt (insertBinaryPackages path (status, packages) state)
+                 return packages
+    where
+      path = rootPath (rootDir cache) ++ indexCacheFile cache repo release index
+
+-- | Return a list of all source packages.
+releaseSourcePackages :: MonadRepoCache m => RepoKey -> Release -> m (Set SourcePackage)
+releaseSourcePackages repo release =
+    mapM (sourcePackagesOfIndex repo release) (sourceIndexList release) >>= return . test
+    where
+      test :: [Either SomeException [SourcePackage]] -> Set SourcePackage
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Return a list of all the binary packages for all supported architectures.
+releaseBinaryPackages :: MonadRepoCache m => RepoKey -> Release -> m (Set BinaryPackage)
+releaseBinaryPackages repo release =
+    mapM (binaryPackagesOfIndex repo release) (binaryIndexList release) >>= return . test
+    where
+      test xs = case partitionEithers xs of
+                  ([], ok) -> Set.unions (List.map Set.fromList ok)
+                  (bad, _) -> error $ intercalate ", " (List.map show bad)
+
+-- | Write a set of packages into a package index.
+putPackages :: LocalRepository -> Release -> PackageIndexLocal ->  [BinaryPackageLocal] -> IO ()
+putPackages repo release index packages =
+    writeAndZipFileWithBackup (outsidePath (repoRoot repo) </> packageIndexPath release index) (L.fromChunks [encodeUtf8 text]) >>= either (fail . intercalate "\n") return
+    where
+      text = T.concat (intersperse (T.pack "\n") . List.map formatParagraph . List.map packageInfo $ packages)
diff --git a/testdata/split6-expected/Debian/Repo/PackageIndex.hs b/testdata/split6-expected/Debian/Repo/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/PackageIndex.hs
@@ -0,0 +1,87 @@
+module Debian.Repo.PackageIndex
+    ( packageIndexName
+    , packageIndexPath
+    , packageIndexDir
+    , packageIndexPathList
+    , packageIndexDirList
+    , packageIndexList
+    , sourceIndexList
+    , binaryIndexList
+    , releaseDir
+    , showIndexBrief
+    , debSourceFromIndex
+    ) where
+
+import Debian.Arch (Arch(..), prettyArch)
+import Debian.Release (releaseName', sectionName')
+import Debian.Repo.Types (PackageIndex(..), Release(..))
+import Debian.Repo.Types.Repo (repoURI)
+import Debian.Repo.Types.Repository (Repository)
+import Debian.Sources (DebSource(..), SourceType(..))
+import System.FilePath ((</>))
+
+packageIndexName :: PackageIndex -> FilePath
+packageIndexName index =
+    case packageIndexArch index of
+      Source -> "Sources"
+      _ -> "Packages"
+
+packageIndexPath :: Release -> PackageIndex -> FilePath
+packageIndexPath release index = packageIndexDir release index ++ "/" ++ packageIndexName index
+
+packageIndexDir :: Release -> PackageIndex -> FilePath
+packageIndexDir release index =
+    case packageIndexArch index of
+      Source -> releaseDir release ++ "/" ++ sectionName' (packageIndexComponent index) ++ "/source"
+      _ -> (releaseDir release ++ "/" ++
+            sectionName' (packageIndexComponent index) ++
+            -- Will prettyArch give us linux-amd64 when we just want amd64?
+            "/binary-" ++ show (prettyArch (packageIndexArch index)))
+
+releaseDir :: Release -> String
+releaseDir release = "dists/" ++ (releaseName' . releaseName $ release)
+
+packageIndexPathList :: Release -> [FilePath]
+packageIndexPathList release = map (packageIndexPath release) . packageIndexList $ release
+
+packageIndexDirList :: Release -> [FilePath]
+packageIndexDirList release = map (packageIndexDir release) . packageIndexList $ release
+
+packageIndexList :: Release -> [PackageIndex]
+packageIndexList release = sourceIndexList release ++ binaryIndexList release
+
+sourceIndexList :: Release -> [PackageIndex]
+sourceIndexList release =
+    map componentIndex (releaseComponents $ release)
+    where componentIndex component = PackageIndex { packageIndexComponent = component
+                                                  , packageIndexArch = Source }
+
+binaryIndexList :: Release -> [PackageIndex]
+binaryIndexList release =
+    concat . map componentIndexes $ (releaseComponents release)
+    where
+      --componentIndexes :: Section -> [PackageIndex]
+      componentIndexes component =
+          map archIndex (filter (/= Source) (releaseArchitectures release))
+          where
+            --archIndex :: Arch -> PackageIndex
+            archIndex arch = PackageIndex { packageIndexComponent = component
+                                          , packageIndexArch = arch }
+
+showIndexBrief :: (Repository, Release) -> PackageIndex -> String
+showIndexBrief release index =
+    (releaseName' . releaseName . snd $ release) </> sectionName' (packageIndexComponent index) </> showArch (packageIndexArch index)
+    where showArch Source = "source"
+          showArch All = "all"
+          showArch x@(Binary _ _) = "binary-" ++ show (prettyArch x)
+
+debSourceFromIndex :: (Repository, Release) -> PackageIndex -> DebSource
+debSourceFromIndex (repo, release) index =
+    DebSource {sourceType = typ,
+               sourceUri = repoURI repo,
+               sourceDist = Right (dist, components)}
+    where
+      typ = case arch of (Binary _ _) -> Deb; Source -> DebSrc; All -> Deb
+      arch = packageIndexArch index
+      dist = releaseName $ release
+      components = releaseComponents $ release
diff --git a/testdata/split6-expected/Debian/Repo/Slice.hs b/testdata/split6-expected/Debian/Repo/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Slice.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE PackageImports, TupleSections #-}
+-- |Types that represent a "slice" of a repository, as defined by a
+-- list of DebSource.  This is called a slice because some sections
+-- may be omitted, and because different repositories may be combined
+-- in the list.
+module Debian.Repo.Slice
+    ( sourceSlices
+    , binarySlices
+    , inexactPathSlices
+    , releaseSlices
+    , appendSliceLists
+    , verifySourceLine
+    , verifySourcesList
+    , repoSources
+    , parseNamedSliceList
+    , parseNamedSliceList'
+    ) where
+
+import Control.Exception (throw)
+import Control.Monad.Trans (liftIO)
+import qualified Data.ByteString.Char8 as B (concat)
+import qualified Data.ByteString.Lazy.Char8 as L (toChunks)
+import Data.List (nubBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (pack, Text, unpack)
+import Debian.Control (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph')
+import Debian.Control.Text (decodeParagraph)
+import Debian.Release (parseReleaseName, parseSection', ReleaseName)
+import Debian.Repo.Monads.Apt (MonadApt)
+import Debian.Repo.SourcesList (parseSourceLine, parseSourcesList)
+import Debian.Repo.Types (EnvPath(..), EnvRoot(..))
+import Debian.Repo.Types.Repo (repoKey, RepoKey(..))
+import Debian.Repo.Types.Repository (prepareRepository)
+import Debian.Repo.Types.Slice (NamedSliceList(..), Slice(..), SliceList(..))
+import Debian.Sources (DebSource(..), SliceName(SliceName), SourceType(..))
+import Debian.URI (dirFromURI, fileFromURI, toURI')
+import Network.URI (URI(uriScheme, uriPath))
+import System.FilePath ((</>))
+import Text.Regex (mkRegex, splitRegex)
+
+sourceSlices :: SliceList -> SliceList
+sourceSlices = SliceList . filter ((== DebSrc) . sourceType . sliceSource) . slices
+
+binarySlices :: SliceList -> SliceList
+binarySlices = SliceList . filter ((== Deb) . sourceType . sliceSource) . slices
+
+inexactPathSlices :: SliceList -> SliceList
+inexactPathSlices = SliceList . filter (either (const False) (const True) . sourceDist . sliceSource) . slices
+
+releaseSlices :: ReleaseName -> SliceList -> SliceList
+releaseSlices release list =
+    SliceList . filter (isRelease . sourceDist . sliceSource) $ (slices list)
+    where isRelease = either (const False) (\ (x, _) -> x == release)
+
+appendSliceLists :: [SliceList] -> SliceList
+appendSliceLists lists =
+    SliceList { slices = concat (map slices lists) }
+
+-- |Examine the repository whose root is at the given URI and return a
+-- set of sources that includes all of its releases.  This is used to
+-- ensure that a package we want to upload doesn't already exist in
+-- the repository.
+repoSources :: MonadApt m => Maybe EnvRoot -> URI -> m SliceList
+repoSources chroot uri =
+    do dirs <- liftIO (uriSubdirs chroot (uri {uriPath = uriPath uri ++ "/dists/"}))
+       releaseFiles <- mapM (liftIO . readRelease uri) dirs >>= return . catMaybes
+       let codenames = map (maybe Nothing (zap (flip elem dirs))) . map (fieldValue "Codename") $ releaseFiles
+           sections = map (maybe Nothing (Just . map parseSection' . splitRegex (mkRegex "[ \t,]+") . unpack) . fieldValue "Components") $ releaseFiles
+           result = concat $ map sources . nubBy (\ (a, _) (b, _) -> a == b) . zip codenames $ sections
+       mapM (verifyDebSource Nothing) result >>= (\ list -> return $ SliceList { slices = list })
+    where
+      sources (Just codename, Just components@(_ : _)) =
+          [DebSource {sourceType = Deb, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)},
+           DebSource {sourceType = DebSrc, sourceUri = uri, sourceDist = Right (parseReleaseName (unpack codename), components)}]
+      sources _ = []
+      -- Compute the list of sections for each dist on a remote server.
+      zap p x = if p x then Just x else Nothing
+
+-- |Return the list of releases in a repository, which is the
+-- list of directories in the dists subdirectory.  Currently
+-- this is only known to work with Apache.  Note that some of
+-- the returned directories may be symlinks.
+uriSubdirs :: (Maybe EnvRoot) -> URI -> IO [Text]
+uriSubdirs root uri =
+    liftIO (dirFromURI uri') >>= either throw (return . map pack)
+    where
+      uri' = case uriScheme uri of
+               "file:" -> uri {uriPath = maybe "" rootPath root ++ (uriPath uri)}
+               _ -> uri
+
+readRelease :: URI -> Text -> IO (Maybe (Paragraph' Text))
+readRelease uri name =
+    do output <- liftIO (fileFromURI uri')
+       case output of
+         Left e -> throw e
+         Right s -> case parseControl (show uri') (B.concat . L.toChunks $ s) of
+                      Right (Control [paragraph]) -> return (Just (decodeParagraph paragraph))
+                      _ -> return Nothing
+    where
+      uri' = uri {uriPath = uriPath uri </> "dists" </> unpack name </> "Release"}
+
+parseNamedSliceList :: MonadApt m => (String, String) -> m (Maybe NamedSliceList)
+parseNamedSliceList (name, text) =
+    (verifySourcesList Nothing . parseSourcesList) text >>=
+    \ sources -> return . Just $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+-- |Create ReleaseCache info from an entry in the config file, which
+-- includes a dist name and the lines of the sources.list file.
+-- This also creates the basic 
+parseNamedSliceList' :: MonadApt m => (String, String) -> m NamedSliceList
+parseNamedSliceList' (name, text) =
+    do sources <- (verifySourcesList Nothing . parseSourcesList) text
+       return $ NamedSliceList { sliceListName = SliceName name, sliceList = sources }
+
+verifySourcesList :: MonadApt m => Maybe EnvRoot -> [DebSource] -> m SliceList
+verifySourcesList chroot list =
+    mapM (verifyDebSource chroot) list >>=
+    (\ xs -> return $ SliceList { slices = xs })
+
+verifySourceLine :: MonadApt m => Maybe EnvRoot -> String -> m Slice
+verifySourceLine chroot str = verifyDebSource chroot (parseSourceLine str)
+
+verifyDebSource :: MonadApt m => Maybe EnvRoot -> DebSource -> m Slice
+verifyDebSource chroot line =
+    repo >>= \ repo' -> return $ Slice {sliceRepoKey = repoKey repo', sliceSource = line}
+    where
+      repo =
+          case uriScheme (sourceUri line) of
+            "file:" -> prepareRepository (Local (EnvPath chroot' (uriPath (sourceUri line))))
+            _ -> prepareRepository (Remote (toURI' (sourceUri line)))
+      chroot' = fromMaybe (EnvRoot "") chroot
diff --git a/testdata/split6-expected/Debian/Repo/SourcesList.hs b/testdata/split6-expected/Debian/Repo/SourcesList.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/SourcesList.hs
@@ -0,0 +1,142 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+--- | A DebSource represents a release of a remote repository and a
+--- method for accessing that repository.
+module Debian.Repo.SourcesList
+    (parseSourceLine,   -- String -> DebSource
+     parseSourceLine',  -- String -> Maybe DebSource
+     parseSourcesList,  -- String -> [DebSource]
+     quoteWords -- String -> [String]
+    )
+    where
+
+import Debian.Release (parseReleaseName, parseSection')
+import Debian.Sources (DebSource(..), SourceType(..))
+import Network.URI (parseURI, unEscapeString)
+
+{-
+
+deb uri distribution [component1] [componenent2] [...]
+
+The URI for the deb type must specify the base of the Debian
+distribution, from which APT will find the information it needs.
+
+distribution can specify an exact path, in which case the components
+must be omitted and distribution must end with a slash (/).
+
+If distribution does not specify an exact path, at least one component
+must be present.
+
+Distribution may also contain a variable, $(ARCH), which expands to
+the Debian architecture (i386, m68k, powerpc, ...)  used on the
+system.
+
+The rest of the line can be marked as a comment by using a #.
+
+Additional Notes:
+
+ + Lines can begin with leading white space.
+
+ + If the dist ends with slash (/), then it must be an absolute path
+   and it is an error to specify components after it.
+
+-}
+
+-- |quoteWords - similar to words, but with special handling of
+-- double-quotes and brackets.
+--
+-- The handling double quotes and [] is supposed to match:
+-- apt-0.6.44.2\/apt-pkg\/contrib\/strutl.cc:ParseQuoteWord()
+--
+-- The behaviour can be defined as:
+--
+--  Break the string into space seperated words ignoring spaces that
+--  appear between \"\" or []. Strip trailing and leading white space
+--  around words. Strip out double quotes, but leave the square
+--  brackets intact.
+quoteWords :: String -> [String]
+quoteWords [] = []
+quoteWords s = quoteWords' (dropWhile (==' ') s)
+    where
+      quoteWords' :: String -> [String]
+      quoteWords' [] = []
+      quoteWords' str =
+          case break (flip elem " [\"") str of
+            ([],[]) -> []
+            (w, []) -> [w]
+            (w, (' ':rest)) -> w : (quoteWords' (dropWhile (==' ') rest))
+            (w, ('"':rest)) ->
+                case break (== '"') rest of
+                  (w',('"':rest)) ->
+                      case quoteWords' rest of
+                        [] ->  [w ++ w']
+                        (w'':ws) -> ((w ++ w' ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing \" in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            (w, ('[':rest)) ->
+                case break (== ']') rest of
+                  (w',(']':rest)) ->
+                      case quoteWords' rest of
+                        []       -> [w ++ "[" ++ w' ++ "]"]
+                        (w'':ws) -> ((w ++ "[" ++ w' ++ "]" ++ w''): ws)
+                  (_w',[]) -> error ("quoteWords: missing ] in the string: "  ++ s)
+                  _ -> error ("the impossible happened in SourcesList.quoteWords")
+            _ -> error ("the impossible happened in SourcesList.quoteWords")
+
+stripLine :: String -> String
+stripLine = takeWhile (/= '#') . dropWhile (== ' ')
+
+sourceLines :: String -> [String]
+sourceLines = filter (not . null) . map stripLine . lines
+
+-- |parseSourceLine -- parses a source line
+-- the argument must be a non-empty, valid source line with comments stripped
+-- see: 'sourceLines'
+parseSourceLine :: String -> DebSource
+parseSourceLine str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Deb
+                          "deb-src" -> DebSrc
+                          o -> error ("parseSourceLine: invalid type " ++ o ++ " in line:\n" ++ str)
+              theUri = case parseURI theUriStr of
+                         Nothing -> error ("parseSourceLine: invalid uri " ++ theUriStr ++ " in the line:\n" ++ str)
+                         Just u -> u
+              theDist = unEscapeString theDistStr
+          in
+            case last theDist of
+              '/' -> if null sections
+                      then DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              _ -> if null sections
+                    then error ("parseSourceLine: Dist is not an exact path, so at least one section is required on the line:\n" ++ str)
+                    else DebSource { sourceType = theType, sourceUri = theUri, sourceDist = Right (parseReleaseName theDist, sections) }
+      _ -> error ("parseSourceLine: invalid line in sources.list:\n" ++ str)
+
+parseSourceLine' :: String -> Maybe DebSource
+parseSourceLine' str =
+    case quoteWords str of
+      (theTypeStr : theUriStr : theDistStr : sectionStrs) ->
+          let sections = map parseSection' sectionStrs
+              theType = case unEscapeString theTypeStr of
+                          "deb" -> Just Deb
+                          "deb-src" -> Just DebSrc
+                          _ -> Nothing
+              theUri = case parseURI theUriStr of
+                         Nothing -> Nothing
+                         Just u -> Just u
+              theDist = unEscapeString theDistStr
+          in
+            case (last theDist, theType, theUri) of
+              ('/', Just typ, Just uri) -> if null sections
+                      then Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Left theDist }
+                      else error ("parseSourceLine: Dist is an exact path, so sections are not allowed on the line:\n" ++ str)
+              (_, Just typ, Just uri) -> if null sections
+                    then Nothing
+                    else Just $ DebSource { sourceType = typ, sourceUri = uri, sourceDist = Right ((parseReleaseName theDist), sections) }
+              _ -> Nothing
+      _ -> Nothing
+
+parseSourcesList :: String -> [DebSource]
+parseSourcesList = map parseSourceLine . sourceLines
diff --git a/testdata/split6-expected/Debian/Repo/Sync.hs b/testdata/split6-expected/Debian/Repo/Sync.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Sync.hs
@@ -0,0 +1,52 @@
+-- Comment above module head
+module Debian.Repo.Sync {-# WARNING "this is a warning" #-}
+    ( rsync
+    , foo
+    -- Comment after last export
+    ) where
+
+-- Comment before first import
+
+import Control.Monad.Trans (MonadIO)
+import qualified Data.ByteString as B (empty)
+import System.Exit (ExitCode)
+import System.FilePath (dropTrailingPathSeparator)
+-- Comment between two imporrts
+import System.Process (proc)
+import System.Process.Progress (keepResult, runProcessF)
+
+-- Comment before first decl
+rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode
+rsync extra source dest =
+    do result <- runProcessF (proc "rsync" (["-aHxSpDt", "--delete"] ++ extra ++
+                                            [dropTrailingPathSeparator source ++ "/",
+                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult
+       case result of
+         [x] -> return x
+         _ -> error "Missing or multiple exit codes"
+
+-- Comment between two decls
+foo :: Int
+foo = 1
+
+{-
+handleExit 1 = "Syntax or usage error"
+handleExit 2 = "Protocol incompatibility"
+handleExit 3 = "Errors selecting input/output files, dirs"
+handleExit 4 = "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server."
+handleExit 5 = "Error starting client-server protocol"
+handleExit 6 = "Daemon unable to append to log-file"
+handleExit 10 = "Error in socket I/O"
+handleExit 11 = "Error in file I/O"
+handleExit 12 = "Error in rsync protocol data stream"
+handleExit 13 = "Errors with program diagnostics"
+handleExit 14 = "Error in IPC code"
+handleExit 20 = "Received SIGUSR1 or SIGINT"
+handleExit 21 = "Some error returned by waitpid()"
+handleExit 22 = "Error allocating core memory buffers"
+handleExit 23 = "Partial transfer due to error"
+handleExit 24 = "Partial transfer due to vanished source files"
+handleExit 25 = "The --max-delete limit stopped deletions"
+handleExit 30 = "Timeout in data send/receive"
+handleExit 35 = "Timeout waiting for daemon connection"
+-}
diff --git a/testdata/split6-expected/Debian/Repo/Types.hs b/testdata/split6-expected/Debian/Repo/Types.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types.hs
@@ -0,0 +1,11 @@
+module Debian.Repo.Types
+    ( module Debian.Repo.Types.AptImage
+    , module Debian.Repo.Types.EnvPath
+    , module Debian.Repo.Types.PackageIndex
+    , module Debian.Repo.Types.Release
+    ) where
+
+import Debian.Repo.Types.AptImage
+import Debian.Repo.Types.EnvPath
+import Debian.Repo.Types.PackageIndex
+import Debian.Repo.Types.Release
diff --git a/testdata/split6-expected/Debian/Repo/Types/AptBuildCache.hs b/testdata/split6-expected/Debian/Repo/Types/AptBuildCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/AptBuildCache.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptBuildCache
+    ( AptBuildCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache)
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+-- | An apt cache with extra sources.list lines for a local upload repository.
+class AptCache t => AptBuildCache t where
+    -- | The sources.list
+    aptSliceList :: t -> SliceList
diff --git a/testdata/split6-expected/Debian/Repo/Types/AptCache.hs b/testdata/split6-expected/Debian/Repo/Types/AptCache.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/AptCache.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptCache
+    ( AptCache(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import Debian.Repo.Types.Slice (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+class (Ord t, Eq t, Show t) => AptCache t where
+    globalCacheDir :: t -> FilePath
+    -- | The directory you might chroot to.
+    rootDir :: t -> EnvRoot
+    -- | The sources.list without the local repository
+    aptBaseSliceList :: t -> SliceList
+    -- | The build architecture
+    aptArch :: t -> Arch
+    -- | Return the all source packages in this AptCache.
+    aptSourcePackages :: t -> [SourcePackage]
+    -- | Return the all binary packages for the architecture of this AptCache.
+    aptBinaryPackages :: t -> [BinaryPackage]
+    -- | Name of release
+    aptReleaseName :: t -> ReleaseName
diff --git a/testdata/split6-expected/Debian/Repo/Types/AptImage.hs b/testdata/split6-expected/Debian/Repo/Types/AptImage.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/AptImage.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.AptImage
+    ( AptImage(..)
+    ) where
+
+import Debian.Arch (Arch(..))
+import Debian.Release (ReleaseName(..))
+import Debian.Repo.Types.AptCache (AptCache(..))
+import Debian.Repo.Types.EnvPath (EnvRoot)
+import Debian.Repo.Types.PackageIndex (BinaryPackage, SourcePackage)
+import qualified Debian.Repo.Types.Slice as T (SliceList)
+
+{-
+instance Show FileStatus where
+    show _ = "def :: FileStatus"
+-}
+---------------------- CACHED OS IMAGE ---------------------
+
+data AptImage =
+    AptImage { aptGlobalCacheDir :: FilePath
+             , aptImageRoot :: EnvRoot
+             , aptImageArch :: Arch
+             , aptImageSliceList :: T.SliceList
+             , aptImageReleaseName :: ReleaseName
+             , aptImageSourcePackages :: [SourcePackage]
+             , aptImageBinaryPackages :: [BinaryPackage]
+             }
+
+instance Show AptImage where
+    show apt = "AptImage " ++ relName (aptImageReleaseName apt)
+
+instance AptCache AptImage where
+    globalCacheDir = aptGlobalCacheDir
+    rootDir = aptImageRoot
+    aptArch = aptImageArch
+    aptBaseSliceList = aptImageSliceList
+    aptSourcePackages = aptImageSourcePackages
+    aptBinaryPackages = aptImageBinaryPackages
+    aptReleaseName = aptImageReleaseName
+
+instance Ord AptImage where
+    compare a b = compare (aptImageReleaseName a) (aptImageReleaseName b)
+
+instance Eq AptImage where
+    a == b = compare a b == EQ
diff --git a/testdata/split6-expected/Debian/Repo/Types/EnvPath.hs b/testdata/split6-expected/Debian/Repo/Types/EnvPath.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/EnvPath.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.EnvPath
+    ( EnvRoot(..)
+    , EnvPath(..)
+    , outsidePath
+    , appendPath
+    , rootEnvPath
+    ) where
+
+-- |The root directory of an OS image.
+data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
+
+-- |A directory inside of an OS image.
+data EnvPath = EnvPath { envRoot :: EnvRoot
+                       , envPath :: FilePath
+                       } deriving (Ord, Eq, Read, Show)
+
+outsidePath :: EnvPath -> FilePath
+outsidePath path = rootPath (envRoot path) ++ envPath path
+
+appendPath :: FilePath -> EnvPath -> EnvPath
+appendPath suff path = path { envPath = envPath path ++ suff }
+
+rootEnvPath :: FilePath -> EnvPath
+rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
diff --git a/testdata/split6-expected/Debian/Repo/Types/PackageIndex.hs b/testdata/split6-expected/Debian/Repo/Types/PackageIndex.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/PackageIndex.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.PackageIndex
+    ( PackageIndex(..)
+    , PackageIndexLocal
+    , PackageID(packageVersion)
+    , prettyPackageID
+    , BinaryPackage(..)
+    , binaryPackageName
+    , prettyBinaryPackage
+    , makeBinaryPackageID
+    , SourcePackage(..)
+    , sourcePackageName
+    , makeSourcePackageID
+    , SourceControl(..)
+    , SourceFileSpec(..)
+    , PackageIDLocal
+    , BinaryPackageLocal
+    , SourcePackageLocal
+    , PackageVersion(..)
+    , PkgVersion(..)
+    , prettyPkgVersion
+    ) where
+
+import Data.Text (Text, map)
+import Debian.Arch (Arch(..))
+import qualified Debian.Control.Text as T (Paragraph)
+import Debian.Relation (BinPkgName(..), SrcPkgName(..))
+import qualified Debian.Relation as B (PkgName, Relations)
+import Debian.Release (Section(..))
+import Debian.Repo.Orphans ({- instances -})
+import Debian.Version (DebianVersion, prettyDebianVersion)
+import System.Posix.Types (FileOffset)
+import Text.PrettyPrint.ANSI.Leijen ((<>), Doc, Pretty(pretty), text)
+
+class (Eq a, Ord a) => PackageVersion a where
+    pkgName :: a -> BinPkgName
+    pkgVersion :: a -> DebianVersion
+
+-- |This is an old type which is still used to interface with the
+-- Debian.Relation module.
+data PkgVersion = PkgVersion { getName :: BinPkgName
+                             , getVersion :: DebianVersion
+                             } deriving (Eq, Ord, Show)
+
+instance PackageVersion PkgVersion where
+    pkgName = getName
+    pkgVersion = getVersion
+
+prettyPkgVersion :: PkgVersion -> Doc
+prettyPkgVersion v = pretty (getName v) <> text "=" <> prettyDebianVersion (getVersion v)
+
+---------------- PACKAGES AND PACKAGE INDEXES -------------
+
+-- |The PackageIndex type represents a file containing control
+-- information about debian packages, either source or binary.
+-- Though the control information for a binary package does not
+-- specify an architecture, the architecture here is that of
+-- the environment where the package information is cached.
+data PackageIndex
+    = PackageIndex { packageIndexComponent :: Section
+                   , packageIndexArch :: Arch
+                   } deriving (Eq, Ord, Show)
+
+type PackageIndexLocal = PackageIndex
+
+prettyBinaryPackage :: BinaryPackage -> Doc
+prettyBinaryPackage p = pretty (pkgName p) <> text "-" <> prettyDebianVersion (pkgVersion p)
+
+makeBinaryPackageID :: String -> DebianVersion -> PackageID BinPkgName
+makeBinaryPackageID n v = PackageID (BinPkgName n) v
+
+makeSourcePackageID :: String -> DebianVersion -> PackageID SrcPkgName
+makeSourcePackageID n v = PackageID (SrcPkgName n) v
+
+instance PackageVersion BinaryPackage where
+    pkgName = binaryPackageName
+    pkgVersion = packageVersion . packageID
+
+-- | The 'PackageID' type fully identifies a package by name, version,
+-- and a 'PackageIndex' which identifies the package's release,
+-- component and architecture.
+
+data PackageID n
+    = PackageID
+      { packageName :: n
+      , packageVersion :: DebianVersion
+      } deriving (Eq, Ord, Show)
+
+binaryPackageName :: BinaryPackage -> BinPkgName
+binaryPackageName = packageName . packageID
+
+sourcePackageName :: SourcePackage -> SrcPkgName
+sourcePackageName = packageName . sourcePackageID
+
+prettyPackageID :: B.PkgName n => PackageID n -> Doc
+prettyPackageID p = pretty (packageName p) <> text "=" <> prettyDebianVersion (packageVersion p)
+
+-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
+-- information obtained from the package index.
+data BinaryPackage
+    = BinaryPackage
+      { packageID :: PackageID BinPkgName
+      , packageInfo :: T.Paragraph
+      , pDepends :: B.Relations
+      , pPreDepends :: B.Relations
+      , pConflicts ::B.Relations
+      , pReplaces :: B.Relations
+      , pProvides :: B.Relations
+      }
+
+instance Ord BinaryPackage where
+    compare a b = compare (packageID a) (packageID b)
+
+instance Eq BinaryPackage where
+    a == b = (packageID a) == (packageID b)
+
+data SourcePackage
+    = SourcePackage
+      { sourcePackageID :: PackageID SrcPkgName
+      , sourceParagraph :: T.Paragraph
+      , sourceControl :: SourceControl
+      , sourceDirectory :: String
+      , sourcePackageFiles :: [SourceFileSpec]
+      } deriving (Show, Eq, Ord)
+
+-- |Source package information derived from the control paragraph.
+data SourceControl
+    = SourceControl
+      { source :: Text
+      , maintainer :: NameAddr
+      , uploaders :: [NameAddr]
+      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
+      , packagePriority :: Maybe Priority
+      , buildDepends :: [Package]
+      , buildDependsIndep :: [Package]
+      , buildConflicts :: [Package]
+      , buildConflictsIndep :: [Package]
+      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
+      , homepage :: Maybe Text -- There are packages that don't have this
+      } deriving (Show, Eq, Ord)
+
+type NameAddr = Text
+type StandardsVersion = Text
+type Section' = Text
+type Priority = Text
+type Package = Text
+
+data SourceFileSpec
+    = SourceFileSpec
+      { sourceFileMD5sum :: String
+      , sourceFileSize :: FileOffset
+      , sourceFileName :: FilePath
+      }
+    deriving (Show, Eq, Ord)
+
+type PackageIDLocal n = PackageID n
+type BinaryPackageLocal = BinaryPackage
+type SourcePackageLocal = SourcePackage
diff --git a/testdata/split6-expected/Debian/Repo/Types/Release.hs b/testdata/split6-expected/Debian/Repo/Types/Release.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/Release.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Release
+    ( Release(Release, releaseName, releaseAliases, releaseArchitectures, releaseComponents)
+    , makeReleaseInfo
+    , parseComponents
+    , parseArchitectures
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure))
+import Data.Text (Text, unpack)
+import Debian.Arch (Arch(..), parseArch)
+import qualified Debian.Control.Text as T (fieldValue, Paragraph)
+import Debian.Release (parseSection', ReleaseName(..), Section(..))
+import Text.Regex (mkRegex, splitRegex)
+import qualified Tmp.File as F (File(..))
+
+-- FIXME: The lists here should be sets so that == and compare work properly.
+data Release = Release { releaseName :: ReleaseName
+                       , releaseAliases :: [ReleaseName]
+                       , releaseArchitectures :: [Arch]
+                       , releaseComponents :: [Section]
+                       } deriving (Eq, Ord, Read, Show)
+
+makeReleaseInfo :: F.File T.Paragraph -> ReleaseName -> [ReleaseName] -> Release
+makeReleaseInfo file@(F.File {F.text = Failure msgs}) _name _aliases =
+    error $ "Failure reading " ++ show (F.path file) ++ ": " ++ show msgs
+makeReleaseInfo file@(F.File {F.text = Success info}) name aliases =
+    case (T.fieldValue "Architectures" info, T.fieldValue "Components" info) of
+      (Just archList, Just compList) ->
+          Release { releaseName = name
+                  , releaseAliases = aliases
+                  , releaseArchitectures = parseArchitectures archList
+                  , releaseComponents = parseComponents compList }
+      _ -> error $ "Missing Architectures or Components field in Release file " ++ show (F.path file)
+
+parseArchitectures :: Text -> [Arch]
+parseArchitectures archList =
+    map parseArch . splitRegex re . unpack $ archList
+    where
+      re = mkRegex "[ ,]+"
+
+parseComponents :: Text -> [Section]
+parseComponents compList =
+    map parseSection' . splitRegex re . unpack  $ compList
+    where
+      re = mkRegex "[ ,]+"
diff --git a/testdata/split6-expected/Debian/Repo/Types/Repo.hs b/testdata/split6-expected/Debian/Repo/Types/Repo.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/Repo.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE FlexibleInstances, StandaloneDeriving, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repo
+    ( Repo(..)
+    , RepoKey(..)
+    , repoURI
+    , repoKeyURI
+    , libraryCompatibilityLevel
+    , compatibilityFile
+    ) where
+
+import Control.Exception (throw)
+import Data.Char (isDigit)
+import Data.Maybe (fromJust)
+import Data.Text (unpack)
+import Debian.Repo.Types.EnvPath (EnvPath(..))
+import Debian.Repo.Types.Release (Release)
+import Debian.URI (fileFromURI, fromURI', URI')
+import qualified Debian.UTF8 as Deb (decode)
+import Network.URI (parseURI, URI(uriPath))
+import System.FilePath ((</>))
+
+data RepoKey
+    = Remote URI'
+    | Local EnvPath
+      deriving (Read, Show, Eq, Ord)
+
+class (Ord t, Eq t) => Repo t where
+    repoKey :: t -> RepoKey
+    repositoryCompatibilityLevel :: t -> IO (Maybe Int)
+    repositoryCompatibilityLevel r =
+        fileFromURI uri' >>= either throw (return . parse . unpack . Deb.decode)
+        where
+          uri' = uri {uriPath = uriPath uri </> compatibilityFile}
+          uri = case repoKey r of
+                  Remote x -> fromURI' x
+                  Local x -> fromJust . parseURI $ "file://" ++ envPath x
+          parse :: String -> Maybe Int
+          parse s = case takeWhile isDigit s of
+                         "" -> Nothing
+                         s' -> Just . read $ s'
+    -- | This method returns a list of all the release in the
+    -- repository.  This can be used to identify all of the files
+    -- in the repository that are not garbage.
+    repoReleaseInfo :: t -> [Release]
+    checkCompatibility :: t -> IO ()
+    checkCompatibility repo =
+        do level <- repositoryCompatibilityLevel repo
+           case level of
+             Nothing -> return ()
+             Just n | n >= libraryCompatibilityLevel -> return ()
+             Just n -> error ("Compatibility error: repository level " ++ show n ++
+                              " < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
+
+-- |The name of the file which holds the repository's compatibility
+-- level.
+compatibilityFile :: FilePath
+compatibilityFile = "repository-compat"
+
+-- | The compatibility level of this library and any applications
+-- which use it.  It is an error if we try to use a repository whose
+-- compatibility level is higher than this, a newer version of the
+-- library must be used.  This value was increased from 1 to 2 due
+-- to a new version number tagging policy.
+libraryCompatibilityLevel :: Int
+libraryCompatibilityLevel = 2
+
+repoURI :: Repo r => r -> URI
+repoURI = repoKeyURI . repoKey
+
+repoKeyURI :: RepoKey -> URI
+repoKeyURI (Local path) = fromJust . parseURI $ "file://" ++ envPath path
+repoKeyURI (Remote uri) = fromURI' uri
diff --git a/testdata/split6-expected/Debian/Repo/Types/Repository.hs b/testdata/split6-expected/Debian/Repo/Types/Repository.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/Repository.hs
@@ -0,0 +1,428 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Repository
+    ( Repository
+    , LocalRepository, repoRoot, repoLayout, repoReleaseInfoLocal
+    , RemoteRepository
+    , Layout(..)
+    , MonadRepoCache(getRepoCache, putRepoCache)
+    , loadRepoCache
+    , saveRepoCache
+    , fromLocalRepository
+    , readLocalRepo
+    , prepareLocalRepository
+    , copyLocalRepo -- repoCD
+    , prepareRepository
+    , setRepositoryCompatibility
+    , flushLocalRepository
+    , poolDir'
+    -- , sliceReleaseNames
+    , repoReleaseNames
+    ) where
+
+import Control.Applicative.Error (Failing(Success, Failure), maybeRead)
+import Control.Exception (ErrorCall(..), SomeException, toException)
+import Control.Monad (filterM, unless, when)
+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IO (catch)
+import Control.Monad.Trans (liftIO, MonadIO)
+import Data.List (groupBy, intercalate, isPrefixOf, partition, sort)
+import Data.Map as Map (empty, fromList, insert, insertWith, lookup, Map, toList, union)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Text as T (Text, unpack)
+import Debian.Changes (ChangedFileSpec(changedFileSection), ChangesFile(changeInfo))
+import qualified Debian.Control.Text as T (Control'(Control), ControlFunctions(parseControl), fieldValue, Paragraph, Paragraph')
+import Debian.Release (parseReleaseName, ReleaseName(..), releaseName', Section, sectionName', SubSection(section))
+import Debian.Repo.Monads.Top (MonadTop, sub)
+import Debian.Repo.Sync (rsync)
+import Debian.Repo.Types.EnvPath (EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
+import Debian.Repo.Types.Release (makeReleaseInfo, Release(releaseName))
+import Debian.Repo.Types.Repo (compatibilityFile, libraryCompatibilityLevel, Repo(..), RepoKey(..))
+import Debian.URI (dirFromURI, fileFromURI, fromURI', toURI', URI(uriScheme, uriPath), URI', uriToString')
+import Debian.UTF8 as Deb (decode)
+import Extra.Files (maybeWriteFile)
+import Extra.List (partitionM)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
+import System.Exit (ExitCode(ExitSuccess))
+import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import qualified System.Posix.Files as F (fileMode, getFileStatus, getSymbolicLinkStatus, isSymbolicLink, readSymbolicLink, removeLink, setFileMode)
+import System.Process.Progress (qPutStr, qPutStrLn, quieter)
+import System.Unix.Directory (removeRecursiveSafely)
+import qualified Text.Format as F (Pretty(..))
+import Text.PrettyPrint.ANSI.Leijen (text)
+import Text.Regex (matchRegex, mkRegex)
+import qualified Tmp.File as F (File(..), readFile, Source(RemotePath))
+
+--------------------- REPOSITORY -----------------------
+
+-- | The Repository type reprents any instance of the Repo class, so
+-- it might be local or remote.
+--data Repository = forall a. (Repo a) => Repository a
+data Repository
+    = LocalRepo LocalRepository
+    | RemoteRepo RemoteRepository
+    deriving (Show, Read)
+
+instance Ord Repository where
+    compare a b = compare (repoKey a) (repoKey b)
+
+instance Eq Repository where
+    a == b = compare a b == EQ
+
+-- | URI has a bogus show function, which we are using here.
+instance F.Pretty URI' where
+    pretty = text . show . fromURI'
+
+instance F.Pretty Repository where
+    pretty (LocalRepo r) = text $ outsidePath (repoRoot r)
+    pretty (RemoteRepo r) = F.pretty r
+
+instance Repo Repository where
+    repoKey (LocalRepo (LocalRepository path _ _)) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoKey (RemoteRepo (RemoteRepository uri _)) = Remote uri
+    repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
+    repoReleaseInfo (RemoteRepo (RemoteRepository _ info)) = info
+
+data LocalRepository
+    = LocalRepository
+      { repoRoot_ :: EnvPath
+      , repoLayout_ :: (Maybe Layout)
+      , repoReleaseInfoLocal_ :: [Release]
+      } deriving (Read, Show)
+
+instance Ord LocalRepository where
+    compare (LocalRepository a _ _) (LocalRepository b _ _) = compare a b
+
+instance Eq LocalRepository where
+    a == b = compare a b == EQ
+
+data RemoteRepository
+    = RemoteRepository URI' [Release]
+    deriving (Read, Show)
+
+instance Ord RemoteRepository where
+    compare (RemoteRepository a _) (RemoteRepository b _) = compare a b
+
+instance Eq RemoteRepository where
+    a == b = compare a b == EQ
+
+instance F.Pretty RemoteRepository where
+    pretty (RemoteRepository s _) = F.pretty s
+
+repoRoot :: LocalRepository -> EnvPath
+repoRoot = repoRoot_
+
+repoLayout :: LocalRepository -> Maybe Layout
+repoLayout = repoLayout_
+
+repoReleaseInfoLocal :: LocalRepository -> [Release]
+repoReleaseInfoLocal = repoReleaseInfoLocal_
+
+-- |The possible file arrangements for a repository.  An empty
+-- repository does not yet have either of these attributes.
+data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
+
+instance Repo LocalRepository where
+    repoKey (LocalRepository path _ _) = Local path -- fromJust . parseURI $ "file://" ++ envPath path
+    repoReleaseInfo (LocalRepository _ _ info) = info
+
+class MonadIO m => MonadRepoCache m where
+    getRepoCache :: m (Map RepoKey Repository)
+    putRepoCache :: Map RepoKey Repository -> m ()
+
+modifyRepoCache :: MonadRepoCache m => (Map RepoKey Repository -> Map RepoKey Repository) -> m ()
+modifyRepoCache f = do
+    s <- getRepoCache
+    putRepoCache (f s)
+
+fromLocalRepository :: LocalRepository -> Repository
+fromLocalRepository = LocalRepo
+
+-- | Load the value of the repo cache map from a file as a substitute for
+-- downloading information from the remote repositories.  These values may
+-- go out of date, as when a new release is added to a repository.  When this
+-- happens some ugly errors will occur and the cache will have to be flushed.
+loadRepoCache :: (MonadRepoCache m, MonadTop m) => m ()
+loadRepoCache =
+    do dir <- sub "repoCache"
+       liftIO (loadRepoCache' dir `catch` (\ (e :: SomeException) -> qPutStrLn (show e) >> return Map.empty)) >>= putRepoCache
+    where
+      loadRepoCache' :: FilePath -> IO (Map RepoKey Repository)
+      loadRepoCache' repoCache =
+          do qPutStrLn "Loading repo cache..."
+             file <- readFile repoCache
+             case maybeRead file of
+               Nothing ->
+                   error ("Ignoring invalid repoCache: " ++ show file)
+               Just pairs ->
+                   qPutStrLn ("Loaded " ++ show (length pairs) ++ " entries from the repo cache.") >>
+                   return (fromList pairs)
+
+-- | Write the repo cache map into a file.
+saveRepoCache :: (MonadIO m, MonadTop m, MonadRepoCache m) => m ()
+saveRepoCache =
+          do path <- sub "repoCache"
+             live <- getRepoCache
+             repoCache <- liftIO $ loadCache path
+             let merged = show . Map.toList $ Map.union live repoCache
+             liftIO (F.removeLink path `IO.catch` (\e -> unless (isDoesNotExistError e) (ioError e))) >> liftIO (writeFile path merged)
+             return ()
+          where
+            -- isRemote uri = uriScheme uri /= "file:"
+            -- isRemote (uri, _) = uriScheme uri /= "file:"
+            loadCache :: FilePath -> IO (Map.Map RepoKey Repository)
+            loadCache path =
+                readFile path `IO.catch` (\ (_ :: SomeException) -> return "[]") >>=
+                return . Map.fromList . fromMaybe [] . maybeRead
+
+readLocalRepo :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+readLocalRepo root layout =
+    do state <- getRepoCache
+       case Map.lookup (Local root) state of
+         Just (RemoteRepo (RemoteRepository _ _)) -> error "readLocalRepo: internal error" -- somehow a remote repo got associated with a Local RepoKey
+         Just (LocalRepo repo) -> return repo
+         Nothing ->
+             do names <- liftIO (getDirectoryContents distDir) >>= return . filter (\ x -> not . elem x $ [".", ".."])
+                (links, dists) <- partitionM (liftIO . isSymLink . (distDir </>)) names
+                linkText <- mapM (liftIO . F.readSymbolicLink) (map (distDir </>) links)
+                let aliasPairs = zip linkText links ++ map (\ dist -> (dist, dist)) dists
+                let distGroups = groupBy fstEq . sort $ aliasPairs
+                let aliases = map (checkAliases  . partition (uncurry (==))) distGroups
+                releaseInfo <- mapM (liftIO . getReleaseInfo) aliases
+                qPutStrLn ("LocalRepository releaseInfo " ++ show root ++ ": " ++ show releaseInfo)
+                let repo = LocalRepository { repoRoot_ = root
+                                           , repoLayout_ = layout
+                                           , repoReleaseInfoLocal_ = releaseInfo }
+                putRepoCache (insertWith (\ _ x -> x) (Local root) (LocalRepo repo) state)
+                return repo
+    where
+      fstEq (a, _) (b, _) = a == b
+      checkAliases :: ([(String, String)], [(String, String)]) -> (ReleaseName, [ReleaseName])
+      checkAliases ([(realName, _)], aliases) = (parseReleaseName realName, map (parseReleaseName . snd) aliases)
+      checkAliases _ = error "Symbolic link points to itself!"
+      getReleaseInfo :: (ReleaseName, [ReleaseName]) -> IO Release
+      getReleaseInfo (dist, aliases) = parseReleaseFile (releasePath dist) dist aliases
+      releasePath dist = distDir </> releaseName' dist ++ "/Release"
+      distDir = outsidePath root ++ "/dists"
+
+isSymLink :: FilePath -> IO Bool
+isSymLink path = F.getSymbolicLinkStatus path >>= return . F.isSymbolicLink
+
+parseReleaseFile :: FilePath -> ReleaseName -> [ReleaseName] -> IO Release
+parseReleaseFile path dist aliases =
+    liftIO (F.readFile path) >>= return . parseRelease dist aliases
+
+parseRelease :: ReleaseName -> [ReleaseName] -> F.File Text -> Release
+parseRelease name aliases file =
+    case F.text file of
+      Failure msgs -> error $ "Could not read " ++ show (F.path file) ++ ": " ++ show msgs
+      Success text ->
+          case T.parseControl (show (F.path file)) text of
+            Left msg -> error $ "Failure parsing " ++ show (F.path file) ++ ": " ++ show msg
+            Right (T.Control []) -> error $ "Empty release file: " ++ show (F.path file)
+            Right (T.Control (info : _)) -> makeReleaseInfo (F.File {F.path = F.path file, F.text = Success info}) name aliases
+
+-- | Create or verify the existance of the directories which will hold
+-- a repository on the local machine.  Verify the index files for each of
+-- its existing releases.
+prepareLocalRepository :: MonadRepoCache m => EnvPath -> Maybe Layout -> m LocalRepository
+prepareLocalRepository root layout =
+    do mapM_ (liftIO . initDir)
+                 [(".", 0o40755),
+                  ("dists", 0o40755),
+                  ("incoming", 0o41755),
+                  ("removed", 0o40750),
+                  ("reject", 0o40750)]
+       layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
+                  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
+       mapM_ (liftIO . initDir)
+                 (case layout' of
+                    Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
+                    Just Flat -> []
+                    Nothing -> [])
+       readLocalRepo root layout'
+    where
+      initDir (name, mode) =
+          do let path = outsidePath root </> name
+             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
+                     mapM_ (\ f -> createDirectoryIfMissing True f)
+             actualMode <- F.getFileStatus path >>= return . F.fileMode
+             when (mode /= actualMode) (F.setFileMode path mode)
+{-      notSymbolicLink root name =
+          getSymbolicLinkStatus (root ++ "/dists/" ++ name) >>= return . not . isSymbolicLink
+      hasReleaseFile root name =
+          doesFileExist (root ++ "/dists/" ++ name ++ "/Release") -}
+
+-- |Change the root directory of a repository.  FIXME: This should
+-- also sync the repository to ensure consistency.
+-- repoCD :: EnvPath -> LocalRepository -> LocalRepository
+-- repoCD path repo = repo { repoRoot_ = path }
+
+copyLocalRepo :: MonadRepoCache m => EnvPath -> LocalRepository -> m LocalRepository
+copyLocalRepo dest repo =
+    do qPutStrLn ("Syncing local repository from " ++ src ++ " -> " ++ dst)
+       liftIO $ createDirectoryIfMissing True (outsidePath dest)
+       result <- liftIO $ rsync [] (outsidePath (repoRoot repo)) (outsidePath dest)
+       case result of
+         ExitSuccess ->
+             do let repo' = repo {repoRoot_ = dest}
+                modifyRepoCache (Map.insert (Local dest) (LocalRepo repo'))
+                return repo'
+         code -> error $ "*** FAILURE syncing local repository " ++ src ++ " -> " ++ dst ++ ": " ++ show code
+    where
+      src = outsidePath (repoRoot repo)
+      dst = outsidePath dest
+
+-- |Try to determine a repository's layout.
+computeLayout :: FilePath -> IO (Maybe Layout)
+computeLayout root =
+    do
+      -- If there are already .dsc files in the root directory
+      -- the repository layout is Flat.
+      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
+      -- If the pool directory already exists the repository layout is
+      -- Pool.
+      isPool <- doesDirectoryExist (root ++ "/pool")
+      case (isFlat, isPool) of
+        (True, _) -> return (Just Flat)
+        (False, True) -> return (Just Pool)
+        _ -> return Nothing
+
+-- | Create or update the compatibility level file for a repository.
+setRepositoryCompatibility :: LocalRepository -> IO ()
+setRepositoryCompatibility r =
+    maybeWriteFile path text
+    where text = show libraryCompatibilityLevel ++ "\n"
+          path = outsidePath (repoRoot r) </> compatibilityFile
+
+-- | Return the subdirectory where a source package with the given
+-- section and name would be installed given the layout of the
+-- repository.
+poolDir :: LocalRepository -> Section -> String -> FilePath
+poolDir r section source =
+    case repoLayout r of
+      Just Pool ->
+          "pool/" ++ sectionName' section </> prefixDir </> source
+              where prefixDir =
+                        if isPrefixOf "lib" source
+                        then take (min 4 (length source)) source
+                        else take (min 1 (length source)) source
+      _ -> ""
+
+-- | Return the subdirectory in the pool where a source package would be
+-- installed.
+poolDir' :: LocalRepository -> ChangesFile -> ChangedFileSpec -> FilePath
+poolDir' repo changes file =
+    case T.fieldValue "Source" (changeInfo changes) of
+      Nothing -> error "No 'Source' field in .changes file"
+      Just source -> poolDir repo (section . changedFileSection $ file) (unpack source)
+
+-- | Remove all the packages from the repository and then re-create
+-- the empty releases.
+flushLocalRepository :: MonadRepoCache m => LocalRepository -> m LocalRepository
+flushLocalRepository r =
+    do liftIO $ removeRecursiveSafely (outsidePath (repoRoot r))
+       prepareLocalRepository (repoRoot r) (repoLayout r)
+
+prepareRepository :: MonadRepoCache m => RepoKey -> m Repository
+prepareRepository key =
+    case key of
+      Local path -> prepareLocalRepository path Nothing >>= return . LocalRepo
+      Remote uri -> prepareRepository' (fromURI' uri)
+
+-- | Prepare a repository, which may be remote or local depending on
+-- the URI.
+prepareRepository' :: MonadRepoCache m => URI -> m Repository
+prepareRepository' uri =
+    do state <- getRepoCache
+       repo <- maybe newRepo return (Map.lookup (Remote (toURI' uri)) state)
+       putRepoCache (Map.insert (Remote (toURI' uri)) repo state)
+       return repo
+    where
+      newRepo =
+             case uriScheme uri of
+               "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= return . LocalRepo
+               -- FIXME: We only want to verifyRepository on demand.
+               _ -> verifyRepository (toURI' uri)
+
+-- |To create a RemoteRepo we must query it to find out the
+-- names, sections, and supported architectures of its releases.
+{-# NOINLINE verifyRepository #-}
+verifyRepository :: MonadRepoCache m => URI' -> m Repository
+verifyRepository uri =
+    do state <- getRepoCache
+       case Map.lookup (Remote uri) state of
+         Just repo -> return repo
+         Nothing ->
+             do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
+                let repo = RemoteRepo (RemoteRepository uri releaseInfo)
+                modifyRepoCache (Map.insert (Remote uri) repo)
+                return repo
+
+-- Nice code to do caching, but I figured out how to fix the old code.
+
+--instance Read URI where
+--    readsPrec _ s = [(fromJust (parseURI s), "")]
+--
+---- |Get the list of releases of a remote repository.
+--getReleaseInfo :: FilePath
+--               -> Bool     -- ^ If False don't look at existing cache
+--               -> URI
+--               -> IO [ReleaseInfo]
+--getReleaseInfo top tryCache uri =
+--    readCache >>= \ cache ->
+--    return (lookup uri cache) >>=
+--    maybe (updateCache cache) return
+--    where
+--      cachePath = top ++ "/repoCache"
+--      readCache :: IO [(URI, [ReleaseInfo])]
+--      readCache = if tryCache
+--                  then try (readFile cachePath >>= return . read) >>= return . either (\ (_ :: SomeException) -> []) id
+--                  else return []
+--      updateCache :: [(URI, [ReleaseInfo])] -> IO [ReleaseInfo]
+--      updateCache pairs = getReleaseInfoRemote uri >>= \ info ->
+--                          writeCache ((uri, info) : pairs) >> return info
+--      writeCache :: [(URI, [ReleaseInfo])] -> IO ()
+--      writeCache pairs = writeFile (show pairs) cachePath
+
+-- |Get the list of releases of a remote repository.
+getReleaseInfoRemote :: URI -> IO [Release]
+getReleaseInfoRemote uri =
+    qPutStr ("(verifying " ++ uriToString' uri ++ ".") >>
+    quieter 2 (dirFromURI distsURI) >>=
+    quieter 2 . either (error . show) verify >>=
+    return . catMaybes >>= 
+    (\ result -> qPutStr ")\n" >> return result)
+    where
+      distsURI = uri {uriPath = uriPath uri </> "dists/"}
+      verify names =
+          do let dists = map parseReleaseName names
+             (releaseFiles :: [F.File (T.Paragraph' Text)]) <- mapM getReleaseFile dists
+             let releasePairs = zip3 (map getSuite releaseFiles) releaseFiles dists
+             return $ map (uncurry3 getReleaseInfo) releasePairs
+      releaseNameField releaseFile = case fmap T.unpack (T.fieldValue "Origin" releaseFile) of Just "Debian" -> "Codename"; _ -> "Suite"
+      getReleaseInfo :: Maybe Text -> (F.File T.Paragraph) -> ReleaseName -> Maybe Release
+      getReleaseInfo Nothing _ _ = Nothing
+      getReleaseInfo (Just dist) _ relname | (parseReleaseName (T.unpack dist)) /= relname = Nothing
+      getReleaseInfo (Just dist) info _ = Just $ makeReleaseInfo info (parseReleaseName (T.unpack dist)) []
+      getSuite :: F.File (T.Paragraph' Text) -> Maybe Text
+      getSuite (F.File {F.text = Success releaseFile}) = T.fieldValue (releaseNameField releaseFile) releaseFile
+      getSuite (F.File {F.text = Failure msgs}) = fail (intercalate "\n" msgs)
+      getReleaseFile :: ReleaseName -> IO (F.File (T.Paragraph' Text))
+      getReleaseFile distName =
+          do qPutStr "."
+             release <- fileFromURI releaseURI
+             let control = either Left (either (Left . toException . ErrorCall . show) Right . T.parseControl (show releaseURI) . Deb.decode) release
+             case control of
+               Right (T.Control [info :: T.Paragraph' Text]) -> return $ F.File {F.path = F.RemotePath releaseURI, F.text = Success info}
+               _ -> error ("Failed to get release info from dist " ++ show (relName distName) ++ ", uri " ++ show releaseURI)
+          where
+            releaseURI = distURI {uriPath = uriPath distURI </> "Release"}
+            distURI = distsURI {uriPath = uriPath distsURI </> releaseName' distName}
+      uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+      uncurry3 f (a, b, c) =  f a b c
+
+repoReleaseNames :: Repository -> [ReleaseName]
+repoReleaseNames (RemoteRepo (RemoteRepository _ rels)) = map releaseName rels
+repoReleaseNames (LocalRepo (LocalRepository _ _ rels)) = map releaseName rels
diff --git a/testdata/split6-expected/Debian/Repo/Types/Slice.hs b/testdata/split6-expected/Debian/Repo/Types/Slice.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Debian/Repo/Types/Slice.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE FlexibleInstances, PackageImports, StandaloneDeriving, ScopedTypeVariables, TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Debian.Repo.Types.Slice
+    ( Slice(..)
+    , SliceList(..)
+    , NamedSliceList(..)
+    ) where
+
+import qualified Debian.Repo.Types.Repo as R (RepoKey)
+import Debian.Sources (DebSource(..), SliceName(..), SourceType(..))
+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), vcat)
+
+data Slice = Slice {sliceRepoKey :: R.RepoKey, sliceSource :: DebSource} deriving (Eq, Ord, Show)
+
+-- | Each line of the sources.list represents a slice of a repository
+data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord, Show)
+
+data NamedSliceList
+    = NamedSliceList { sliceList :: SliceList
+                     , sliceListName :: SliceName
+                     } deriving (Eq, Ord, Show)
+
+instance Pretty SliceList where
+    pretty = vcat . map (pretty . sliceSource) . slices
+
+deriving instance Show SourceType
+deriving instance Show DebSource
diff --git a/testdata/split6-expected/IO.hs b/testdata/split6-expected/IO.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/IO.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses,
+             PackageImports, TypeSynonymInstances, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |AptIO is an instance of the RWS monad used to manage the global
+-- state and output style parameters of clients of the Apt library,
+-- such as the autobuilder.
+module IO
+    ( countTasks
+    ) where
+
+import Control.Monad.State (MonadIO)
+import System.Process.Progress (ePutStrLn)
+import Text.Printf (printf)
+
+-- | Perform a list of tasks with log messages.
+countTasks :: MonadIO m => [(String, m a)] -> m [a]
+countTasks tasks =
+    mapM (countTask (length tasks)) (zip [1..] tasks)
+    where
+      countTask :: MonadIO m => Int -> (Int, (String, m a)) -> m a
+      countTask count (index, (message, task)) =
+          ePutStrLn (printf "[%2d of %2d] %s:" index count message) >> task
diff --git a/testdata/split6-expected/Text/Format.hs b/testdata/split6-expected/Text/Format.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Text/Format.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Text.Format where
+
+import Text.PrettyPrint.ANSI.Leijen (Doc, text)
+
+-- | This is a private Pretty class that doesn't have built-in instances
+-- for tuples or lists or anything else.
+class Pretty a where
+    pretty :: a -> Doc
+
+instance Pretty a => Pretty [a] where
+  pretty = text . show .map pretty
diff --git a/testdata/split6-expected/Tmp/File.hs b/testdata/split6-expected/Tmp/File.hs
new file mode 100644
--- /dev/null
+++ b/testdata/split6-expected/Tmp/File.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Tmp.File
+    ( File (File, path, text)
+    , Source (LocalPath, RemotePath)
+    , Tmp.File.readFile
+    ) where
+
+import Control.Applicative ((<$>), Applicative((<*>)))
+import Control.Applicative.Error (Failing(Success, Failure))
+import Control.Exception (SomeException, try)
+import qualified Data.Text as T (Text)
+import qualified Data.Text.IO as T (readFile)
+import Network.URI (URI)
+
+data Source = LocalPath FilePath | RemotePath URI
+
+-- |A file whose contents have been read into memory.
+data File a = File { path :: Source, text :: Failing a }
+
+readFile :: FilePath -> IO (File T.Text)
+readFile x = File <$> return (LocalPath x) <*> (try (T.readFile x) >>= return . either (\ (e :: SomeException) -> Failure [show e]) Success)
+
+instance Show Source where
+    show (LocalPath p) = p
+    show (RemotePath uri) = show uri
