diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/fixfile.cabal b/fixfile.cabal
new file mode 100644
--- /dev/null
+++ b/fixfile.cabal
@@ -0,0 +1,69 @@
+-- Initial fixfile.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                   fixfile
+version:                0.1.0.0
+synopsis:               File-backed recursive data structures.
+homepage:               https://github.com/revnull/fixfile
+license:                LGPL-3
+license-file:           LICENSE
+author:                 Rev. Johnny Healey
+maintainer:             Rev. Johnny Healey <rev.null@gmail.com>
+-- copyright:           
+category:               Data
+build-type:             Simple
+-- extra-source-files:  
+cabal-version:          >=1.10
+description:
+    This package is a library for datatype-generic disk serialization. It
+    provides a file abstraction that uses multi-version concurrency control
+    to support transaction-aware isolation for multi-threaded access.
+
+    The base library comes with a collection of datatypes to provide sets and
+    key-value stores with different properties.
+
+library
+  build-depends:        base >=4.8 && < 5
+                       ,binary
+                       ,bytestring
+                       ,mtl
+                       ,array
+                       ,directory
+                       ,filepath
+                       ,temporary
+                       ,hashable
+                       ,hashtables
+                       ,containers
+                       ,lens
+  hs-source-dirs:       src
+  default-language:     Haskell2010
+  exposed-modules:      Data.FixFile
+                       ,Data.FixFile.BTree
+                       ,Data.FixFile.Set
+                       ,Data.FixFile.Tree23
+                       ,Data.FixFile.Trie
+  other-modules:        Data.FixFile.Fixed
+  ghc-options:          -Wall
+
+Test-Suite test-fixfile
+  hs-source-dirs:       tests 
+  Main-is:              Tests.hs
+  type:                 exitcode-stdio-1.0
+  Build-Depends:        base >=4.5
+                       ,fixfile
+                       ,binary >= 0.7
+                       ,bytestring >= 0.10.2
+                       ,mtl
+                       ,temporary
+                       ,directory
+                       ,QuickCheck
+                       ,tasty
+                       ,tasty-quickcheck
+                       ,exceptions
+                       ,lens
+  default-language:     Haskell2010
+  ghc-options:  -O2 -threaded
+
+source-repository head
+  type: git
+  location: git@github.com:revnull/fixfile.git
diff --git a/src/Data/FixFile.hs b/src/Data/FixFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile.hs
@@ -0,0 +1,491 @@
+{-# LANGUAGE ScopedTypeVariables, RankNTypes, KindSignatures,
+    MultiParamTypeClasses, FlexibleInstances, FlexibleContexts,
+    FunctionalDependencies, TypeFamilies, UndecidableInstances,
+    DeriveDataTypeable, DeriveGeneric #-}
+
+{- |
+    
+    Module      :  Data.FixFile
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    A 'FixFile' is file for storing recursive data. The file supports MVCC
+    through an append-only file.
+
+    In order to eliminate distinctions between data structures that are
+    file-backed versus in-memory, this library makes heavy use of lazy IO.
+    Transactions are used to ensure safety of the unsafe IO.
+
+    The data structures used by a 'FixFile' should not be recursive directly,
+    but should have instances of 'Foldable', 'Traversable', and 'Binary' and
+    should be structured such that the fixed point of the data type is
+    recursive.
+
+    There is also the concept of the 'Root' data of a 'FixFile'.  This can be
+    used as a kind of header for a FixFile that can allow several recursive
+    data structures to be modified in a single transaction.
+
+ -}
+
+module Data.FixFile (
+                      -- * Fixed point combinators
+                      Fixed(..)
+                     ,Fix(..)
+                     ,Stored
+                     -- * F-Algebras
+                     ,CataAlg
+                     ,cata
+                     ,AnaAlg
+                     ,ana
+                     ,ParaAlg
+                     ,para
+                     ,iso
+                     -- * Root Data
+                     ,Root(..)
+                     ,Ptr
+                     ,Ref(..)
+                     ,ref
+                     -- * FixFiles
+                     ,FixFile
+                     ,createFixFile
+                     ,createFixFileHandle
+                     ,openFixFile
+                     ,openFixFileHandle
+                     ,closeFixFile
+                     ,vacuum
+                     -- * Transactions
+                     ,Transaction
+                     ,alterT
+                     ,lookupT
+                     ,readTransaction
+                     ,writeTransaction
+                     ,subTransaction
+                     ,getFull
+                     ) where
+
+import Prelude hiding (sequence, mapM, lookup)
+
+import Control.Concurrent.MVar
+import Control.Exception
+import Control.Lens hiding (iso, para)
+import qualified Control.Monad.RWS as RWS
+import Control.Monad.Identity hiding (mapM)
+import Control.Monad.Trans
+import Data.Binary
+import Data.ByteString.Lazy as BSL
+import Data.Dynamic
+import Data.Hashable
+import Data.HashTable.IO
+import qualified Data.Map as M
+import Data.Maybe
+import Data.Monoid
+import Data.Traversable (mapM)
+import GHC.Generics
+import System.FilePath
+import System.Directory
+import System.IO
+import System.IO.Unsafe
+
+import Data.FixFile.Fixed
+
+type HashTable k v = CuckooHashTable k v
+
+data Cache f = Cache Int (HashTable (Ptr f) (f (Ptr f)))
+    (HashTable (Ptr f) (f (Ptr f)))
+    deriving (Typeable)
+
+type Caches = M.Map TypeRep Dynamic
+
+createCache :: IO (Cache f)
+createCache = Cache 0 <$> new <*> new
+
+cacheInsert :: Ptr f -> f (Ptr f) -> Cache f -> IO (Cache f)
+cacheInsert p f (Cache i oc nc) =
+    if i >= 50
+        then new >>= cacheInsert p f . Cache 0 nc
+        else do
+            insert nc p f
+            return (Cache (i + 1) oc nc)
+
+cacheLookup :: Ptr f -> Cache f -> IO (Cache f, Maybe (f (Ptr f)))
+cacheLookup p c@(Cache _ oc nc) = do
+    nval <- lookup nc p
+    val <- maybe (lookup oc p) (return . Just) nval
+    case (nval, val) of
+        (Nothing, Just v) -> do
+            c' <- cacheInsert p v c
+            return (c', val)
+        _ -> return (c, val)
+
+getCachedOrStored :: Typeable f => Ptr f -> IO (f (Ptr f)) -> MVar Caches ->
+    IO (f (Ptr f))
+getCachedOrStored p m cs = do
+    mval <- withCache cs (cacheLookup p)
+    case mval of
+        Just v -> return v
+        Nothing -> do
+            v <- m
+            withCache_ cs (cacheInsert p v)
+            return v
+
+withCache :: Typeable c => MVar Caches -> (Cache c -> IO (Cache c, a)) -> IO a
+withCache cs f = modifyMVar cs $ \cmap -> do
+    let mc = M.lookup mt cmap >>= fromDynamic
+        mt = typeOf $ fromJust mc
+    c <- maybe createCache return mc
+    (c', a) <- f c
+    return (M.insert mt (toDyn c') cmap, a)
+
+withCache_ :: Typeable c => MVar Caches -> (Cache c -> IO (Cache c)) -> IO ()
+withCache_ cs f = withCache cs $ \c -> f c >>= \c' -> return (c', ())
+
+type Pos = Word64
+
+-- FFH is a FixFile Handle. This is an internal data structure.
+data FFH = FFH (MVar Handle) (MVar Caches)
+
+getRawBlock :: Binary a => Handle -> Pos -> IO a
+getRawBlock h p = do
+    hSeek h AbsoluteSeek (fromIntegral p)
+    (sb :: Word32) <- decode <$> (BSL.hGet h 4)
+    decode <$> BSL.hGet h (fromIntegral sb)
+
+getBlock :: (Typeable f, Binary (f (Ptr f))) => (Ptr f) -> FFH -> IO (f (Ptr f))
+getBlock p@(Ptr pos) (FFH mh mc) = getCachedOrStored p readFromFile mc where
+    readFromFile = withMVar mh $ flip getRawBlock pos
+
+putRawBlock :: Binary a => a -> FFH -> IO Pos
+putRawBlock a (FFH mh _) = putRaw where
+    putRaw = withMVar mh $ \h -> do
+        hSeek h SeekFromEnd 0
+        p <- fromIntegral <$> hTell h
+        let enc  = encode a
+            len  = fromIntegral $ BSL.length enc
+            len' = encode (len :: Word32)
+            enc' = mappend len' enc
+        BSL.hPut h enc'
+        return p
+
+putBlock :: (Typeable f, Binary (f (Ptr f))) => (f (Ptr f)) -> FFH ->
+    IO (Ptr f)
+putBlock a h@(FFH _ mc) = putRawBlock a h >>= cacheBlock . Ptr where
+    cacheBlock p = do
+        withCache_ mc (cacheInsert p a)
+        return p
+
+{- | 
+    'Stored' is a fixed-point combinator of 'f' in Transaction 's'.
+-}
+data Stored s f =
+    -- | A memory-only instance of 'a'.
+    Memory (f (Stored s f))
+    -- | An instance of 'a' that is file-backed.
+  | Cached {-# UNPACK #-} !(Ptr f) (f (Stored s f))
+
+instance Fixed (Stored s) where
+    inf = Memory
+    {-# INLINE inf #-}
+    outf (Memory a) = a
+    outf (Cached _ a) = a
+    {-# INLINE outf #-}
+
+-- | Write the stored data to disk so that the on-disk representation 
+--   matches what is in memory.
+sync :: (Traversable f, Binary (f (Ptr f)), Typeable f) =>
+    FFH -> Stored s f -> IO (Ptr f)
+sync h = commit where
+    commit (Memory r) = do
+        r' <- mapM commit r
+        putBlock r' h
+    commit (Cached p _) = return p
+
+{- |
+    A 'Ptr' points to a location in a 'FixFile' and has a phantom type for a 
+    'Functor' 'f'. A 'Root' expects an argument that resembles a 'Fixed',
+    but we can pass it a 'Ptr' instead. This is not a well-formed 'Fixed'
+    because it can't be unpacked into @'f' ('Ptr' 'f')@.
+    
+    But, it can be serialized, which allows a 'Root' object that takes this
+    as an argument to be serialized.
+-}
+newtype Ptr (f :: * -> *) = Ptr Pos
+    deriving (Generic, Eq, Ord, Read, Show)
+
+instance Binary (Ptr f)
+
+instance Hashable (Ptr f) where
+    hashWithSalt x (Ptr y) = hashWithSalt x y
+
+{- |
+    A 'Root' datastructure acts as a kind of header that can contain one or
+    more 'Ref's to different recursive structures. It takes one argument,
+    which has the kind of @((* -> *) -> *)@. This argument should be either an
+    instance of 'Fixed' or a 'Ptr'. If it is an instance of 'Fixed', then
+    the 'Root' can contain recursive data structures. If it is passed 'Ptr'
+    as an argument, then the 'Root' will contain a non-recursive structure,
+    but can be serialized.
+
+-}
+class Root (r :: (((* -> *) -> *) -> *)) where
+    -- | Deserialize @'r' 'Ptr'@ inside a 'Transaction'.
+    readRoot :: r Ptr -> Transaction r' s (r (Stored s))
+    -- | Serialize @'r' 'Ptr'@ inside a 'Transaction'. This will result in
+    -- | changes to any recursive structures to be written as well.
+    writeRoot :: r (Stored s) -> Transaction r' s (r Ptr)
+
+    -- | 'iso', but applied to an instance of 'Root'.
+    rootIso :: (Fixed g, Fixed h) => r g -> r h
+
+{- |
+    A 'Ref' is a reference to a 'Functor' 'f' in the 'Fixed' instance of 'g'.
+
+    This is an instance of 'Root' and acts to bridge between the 'Root' and
+    the recursively defined data structure that is @('g' 'f')@.
+-}
+data Ref (f :: * -> *) (g :: (* -> *) -> *) = Ref { deRef :: g f }
+    deriving (Generic)
+
+instance (Typeable f, Binary (f (Ptr f)), Traversable f) => Root (Ref f) where
+    readRoot (Ref p) = Ref <$> (withHandle $ flip readStoredLazy p)
+    writeRoot (Ref a) = Ref <$> (withHandle $ flip sync a)
+    rootIso = Ref . iso . deRef
+
+instance Binary (Ref f Ptr)
+
+-- | Lens for accessing the value stored in a Ref
+ref :: Lens' (Ref f g) (g f)
+ref = lens (\(Ref a) -> a) (\_ b -> Ref b)
+
+{- |
+    A 'Transaction' is an isolated execution of a read or update operation
+    on the root object stored in a 'FixFile'. 'r' is the 'Root' data that is
+    stored by the 'FixFile'. 's' is a phantom type used to isolate 'Stored'
+    values to the transaction where they are run.
+-}
+newtype Transaction r s a = Transaction {
+    runRT :: RWS.RWST FFH (Last (r Ptr)) (r (Stored s)) IO a
+  }
+
+instance Functor (Transaction f s) where
+    fmap f (Transaction t) = Transaction $ fmap f t
+
+instance Applicative (Transaction f s) where
+    pure = Transaction . pure
+    Transaction a <*> Transaction b = Transaction $ a <*> b
+
+instance Monad (Transaction f s) where
+    return = pure
+    Transaction t >>= f = Transaction $ RWS.RWST $ \ffh root -> do
+        (a, root', w) <- RWS.runRWST t ffh root
+        (a', root'', w') <- RWS.runRWST (runRT $ f a) ffh root'
+        return (a', root'', w `mappend` w')
+
+instance RWS.MonadState (r (Stored s)) (Transaction r s) where
+    get = Transaction $ RWS.get
+    put = Transaction . RWS.put
+    state = Transaction . RWS.state
+
+{- |
+    Perform a 'Transaction' on a part of the root object.
+-}
+subTransaction :: Lens' (r (Stored s)) (r' (Stored s)) -> Transaction r' s a ->
+    Transaction r s a
+subTransaction l st = Transaction $ RWS.RWST $ \ffh root -> do
+    (a, r, _) <- RWS.runRWST (runRT st) ffh (root^.l)
+    return (a, set l r root, mempty)
+
+withHandle :: (FFH -> IO a) -> Transaction r s a
+withHandle f = Transaction $ RWS.ask >>= liftIO . f
+
+readStoredLazy :: (Traversable f, Binary (f (Ptr f)), Typeable f) =>
+    FFH -> Ptr f -> IO (Stored s f)
+readStoredLazy h p = do
+    f <- getBlock p h
+    let fcons = Cached p
+    fcons <$> mapM (unsafeInterleaveIO . readStoredLazy h) f
+
+{- |
+    The preferred way to modify the root object of a 'FixFile' is by using
+    'alterT'. It applies a function that takes the root object as a
+    @'Stored' 's' 'f'@ and returns the new desired head of the
+    same type.
+-}
+alterT :: (tr ~ Transaction (Ref f) s, Traversable f, Binary (f (Ptr f))) =>
+    (Stored s f -> Stored s f) -> tr ()
+alterT f = ref %= f
+
+{- |
+    The preferred way to read from a 'FixFile' is to use 'lookupT'. It
+    applies a function that takes a @'Stored' s f@ and returns a value.
+-}
+lookupT :: (tr ~ Transaction (Ref f) s, Traversable f, Binary (f (Ptr f))) =>
+    (Stored s f -> a) -> tr a
+lookupT f = f <$> use ref
+
+{- |
+    A 'FixFile' is a handle for accessing a file-backed recursive data
+    structure. 'r' is the 'Root' object stored in the 'FixFile'.
+-}
+data FixFile r = FixFile FilePath (MVar (FFH, r Ptr)) (MVar ())
+
+acquireWriteLock :: FixFile f -> IO ()
+acquireWriteLock (FixFile _ _ wl) = do
+    void $ takeMVar wl
+
+releaseWriteLock :: FixFile f -> IO ()
+releaseWriteLock (FixFile _ _ wl) = do
+    putMVar wl ()
+
+withWriteLock :: FixFile f -> IO a -> IO a
+withWriteLock ff f = do
+    acquireWriteLock ff
+    f `finally` releaseWriteLock ff
+
+readHeader :: FFH -> IO (Pos)
+readHeader (FFH mh _) = withMVar mh $ \h -> do
+    hSeek h AbsoluteSeek 0
+    decode <$> BSL.hGet h 8
+
+updateHeader :: Pos -> Transaction r s ()
+updateHeader p = do
+    withHandle $ \(FFH mh _) -> 
+        withMVar mh $ \h -> do
+            hSeek h AbsoluteSeek 0
+            BSL.hPut h (encode p)
+            hFlush h
+
+{- |
+    Create a 'FixFile', using @'Fix' f@ as the initial structure to store
+    at the location described by 'FilePath'.
+-}
+createFixFile :: (Root r, Binary (r Ptr), Typeable r) =>
+    r Fix -> FilePath -> IO (FixFile r)
+createFixFile initial path =
+    openFile path ReadWriteMode >>= createFixFileHandle initial path
+
+{- |
+    Create a 'FixFile', using @'Fix' f@ as the initial structure to store
+    at the location described by 'FilePath' and using the 'Handle' to the
+    file to be created.
+-}
+createFixFileHandle :: (Root r, Binary (r Ptr), Typeable r) =>
+    r Fix -> FilePath -> Handle -> IO (FixFile r)
+createFixFileHandle initial path h = do
+    ffh <- FFH <$> newMVar h <*> newMVar M.empty
+    BSL.hPut h (encode (0 :: Pos))
+    let t = runRT $ do
+            dr <- writeRoot $ rootIso initial
+            (withHandle $ putRawBlock dr) >>= updateHeader
+            Transaction . RWS.tell . Last . Just $ dr
+    (_,_,root') <- RWS.runRWST t ffh undefined
+    let Just root = getLast root'
+    ffhmv <- newMVar (ffh, root)
+    FixFile path ffhmv <$> newMVar ()
+
+{- |
+    Open a 'FixFile' from the file described by 'FilePath'.
+-}
+openFixFile :: Binary (r Ptr) => FilePath -> IO (FixFile r)
+openFixFile path =
+    openFile path ReadWriteMode >>= openFixFileHandle path
+
+{- |
+    Open a 'FixFile' from the file described by 'FilePath' and using the
+    'Handle' to the file.
+-}
+openFixFileHandle :: Binary (r Ptr) => FilePath -> Handle ->
+    IO (FixFile r)
+openFixFileHandle path h = do
+    ffh <- FFH <$> newMVar h <*> newMVar M.empty
+    root <- readHeader ffh >>= getRawBlock h 
+    ffhmv <- newMVar (ffh, root)
+    FixFile path ffhmv <$> newMVar ()
+
+{- |
+    Close a 'FixFile'. This can potentially cause errors on data that is lazily
+    being read from a 'Transaction'.
+-}
+closeFixFile :: FixFile r -> IO ()
+closeFixFile (FixFile path tmv _) = do
+    (FFH mh _, _) <- takeMVar tmv
+    h <- takeMVar mh
+    hClose h
+    putMVar mh $ error (path ++ " is closed.")
+    putMVar tmv $ error (path ++ " is closed.")
+
+{- |
+    Perform a read transaction on a 'FixFile'. This transaction cannot
+    modify the root object stored in the file. The returned value is lazily
+    evaluated, but will always correspond to the root object at the start
+    of the transaction.
+-}
+readTransaction :: Root r => FixFile r ->
+    (forall s. Transaction r s a) -> IO a
+readTransaction (FixFile _ ffhmv _) t = do
+    (ffh, root) <- readMVar ffhmv
+    let t' = readRoot root >>= RWS.put >> t
+    (a, _) <- RWS.evalRWST (runRT t') ffh undefined
+    return a
+
+{- |
+    Perform a write transaction on a 'FixFile'. This operation differs from
+    the readTransaction in that the root object stored in the file can
+    potentially be updated by this 'Transaction'.
+-}
+writeTransaction :: (Root r, Binary (r Ptr), Typeable r) => 
+    FixFile r -> (forall s. Transaction r s a)
+    -> IO a
+writeTransaction ff@(FixFile _ ffhmv _) t = res where
+    res = withWriteLock ff runTransaction
+    runTransaction = do
+        (ffh, root) <- readMVar ffhmv
+        let t' = readRoot root >>= RWS.put >> t >>= save
+            save a = do
+                dr <- RWS.get >>= writeRoot
+                (withHandle $ putRawBlock dr) >>= updateHeader
+                Transaction . RWS.tell . Last . Just $ dr
+                return a
+        (a, root') <- RWS.evalRWST (runRT t') ffh undefined
+        case getLast root' of
+            Nothing -> return ()
+            Just root'' -> do
+                void $ swapMVar ffhmv (ffh, root'')
+        return a
+
+{- |
+    Get the full datastructure from the transaction as a @'Fix' f@.
+-}
+getFull :: Functor f => Transaction (Ref f) s (Fix f)
+getFull = uses ref iso
+
+{- |
+    Because a 'FixFile' is backed by an append-only file, there is a periodic
+    need to 'vacuum' the file to garbage collect data that is no longer
+    referenced from the root. This task operates on a temporary file that then
+    replaces the file that backs FixFile.
+
+    The memory usage of this operation scales with the recursive depth of the
+    structure stored in the file.
+-}
+vacuum :: (Root r, Binary (r Ptr), Typeable r) =>
+    FixFile r -> IO ()
+vacuum ff@(FixFile path mv _) = withWriteLock ff runVacuum where
+    runVacuum = do
+        mval <- takeMVar mv
+
+        readFFHMV <- newMVar mval
+        readDB <- FixFile path readFFHMV <$> newMVar ()
+
+        (tp, th) <- openTempFile (takeDirectory path) ".ffile.tmp"
+        hClose th
+
+        rootMem <- readTransaction readDB (rootIso <$> RWS.get)
+        (FixFile _ newMV _) <- createFixFile rootMem tp
+
+        renameFile tp path
+
+        takeMVar newMV >>= putMVar mv
+    
diff --git a/src/Data/FixFile/BTree.hs b/src/Data/FixFile/BTree.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile/BTree.hs
@@ -0,0 +1,255 @@
+{-# LANGUAGE DeriveGeneric, DeriveFunctor, DeriveFoldable, DeriveTraversable,
+    DeriveDataTypeable #-}
+
+{- |
+    Module      :  Data.FixFile.BTree
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    This is a BTree data type that can be used with 'FixFile'. It can be used
+    as a key-value store where the same key can correspond to multiple values.
+    It supports logarithmic insert, lookup, and delete operations.
+-}
+module Data.FixFile.BTree (BTree
+                          ,createBTreeFile
+                          ,openBTreeFile
+                          ,empty
+                          ,insertBTree
+                          ,insertBTreeT
+                          ,lookupBTree
+                          ,lookupBTreeT
+                          ,filterBTree
+                          ,filterBTreeT
+                          ,deleteBTree
+                          ,deleteBTreeT
+                          ,toListBTree
+                          ,fromListBTree
+                          ) where
+
+import Data.Array
+import Data.Binary
+import Data.Dynamic
+import GHC.Generics
+
+import Data.FixFile
+
+{- |
+    A 'Fixed' @('BTree' k v)@ stores a BTree of key/value pairs.
+-}
+data BTree k v a =
+    Empty
+  | Value v
+  | Node Word32 (Array Int (k, a))
+    deriving (Read, Show, Generic, Functor, Foldable, Traversable, Typeable)
+
+instance (Binary k, Binary v, Binary a) => Binary (BTree k v a)
+
+-- | An empty 'BTree' 
+empty :: Fixed g => g (BTree k v)
+empty = inf Empty
+
+value :: Fixed g => v -> g (BTree k v)
+value = inf . Value
+
+node :: Fixed g => Word32 -> Array Int (k, g (BTree k v)) -> g (BTree k v)
+node d = inf . Node d
+
+-- | Create a 'FixFile' storing a @('BTree' k v)@.
+--   The initial value is 'empty'.
+createBTreeFile :: (Binary k, Typeable k, Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (BTree k v)))
+createBTreeFile fp = createFixFile (Ref empty) fp
+
+-- | Open a 'FixFile' storing a @('BTree' k v)@.
+openBTreeFile :: (Binary k, Typeable k, Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (BTree k v)))
+openBTreeFile = openFixFile
+
+nodeSize :: Integral i => i
+nodeSize = 32
+
+lookupPos :: (Ord k) => Bool -> k -> Array Int (k, v) ->
+    (Int, [(k, v)], (k, v), [(k, v)])
+lookupPos ff k arr = result . findFirst . uncurry binary $ bounds arr where
+    result i =
+        let (a, b:c) = splitAt i $ elems arr
+        in (i, a, b, c)
+    lookupi = fst . (arr !)
+    findFirst = if ff then findFirst' else id
+    findFirst' 0 = 0
+    findFirst' i = if lookupi (i - 1) == k
+        then findFirst' (i - 1)
+        else i
+    binary mini maxi = 
+        let avg = (maxi + mini) `div` 2
+            avgi = lookupi avg
+        in case (maxi - mini <= 1, compare k avgi) of 
+            (True, _) -> if lookupi maxi <= k then maxi else mini
+            (_, EQ) -> avg
+            (_, LT) -> binary mini (avg - 1)
+            (_, _) -> binary avg maxi
+
+splitRange :: (Ord k) => k -> Array Int (k, v) ->
+    ([(k,v)], [(k,v)], [(k,v)])
+splitRange k = uncurry splitMax . splitMin id Nothing . elems where
+    splitMin f Nothing [] = (f [], [])
+    splitMin f (Just t) [] = (f [], [t])
+    splitMin f Nothing xl@(xt@(xk,_):xs) = case compare xk k of
+        LT -> splitMin f (Just xt) xs
+        _ -> (f [], xl)
+    splitMin f (Just t) xl@(xt@(xk,_):xs) = case compare xk k of
+        LT -> splitMin (f . (t:)) (Just xt) xs
+        _ -> (f [], t:xl)
+    splitMax p xs = 
+        let (c, n) = splitMax' id xs
+        in (p, c, n)
+    splitMax' f [] = (f [], [])
+    splitMax' f xl@(xt@(xk,_):xs) = case compare xk k of
+        GT -> (f [], xl)
+        _ -> splitMax' (f . (xt:)) xs
+
+data Insert k v g =
+    Inserted k (g (BTree k v))
+  | Split Word32 (k, (g (BTree k v))) (k, (g (BTree k v)))
+
+-- | Insert the value 'v' with the key 'k' into a 'Fixed' @('BTree' k v)@.
+insertBTree :: (Ord k, Fixed g) => k -> v -> g (BTree k v) -> g (BTree k v)
+insertBTree k v = merge . para phi where
+    merge (Inserted _ x) = x
+    merge (Split d lt rt) = node (d + 1) $ array (0, 1)
+        [(0, lt), (1, rt)]
+    
+    newNode d c ls = if c > nodeSize
+        then
+            let (l, r) = splitAt half ls
+                half = nodeSize `div` 2
+                half' = c - half
+                mini = fst . head
+            in Split d (mini l, node d $ array (0, half - 1) $ zip [0..] l)
+                (mini r, node d $ array (0, half' - 1) $ zip [0..] r)
+        else Inserted (fst $ head ls) (node d $ array (0, c-1) $ zip [0..] ls)
+
+    children xs = [(i, x) | (i, (x, _)) <- xs]
+
+    phi Empty = Inserted k $ node 0 $ array (0,0) [(0, (k, value v))]
+    phi (Value _) = error "insertBTree phi Value error"
+    phi (Node 0 a) =
+        let (_, p, (kc, (km, _)), n) = lookupPos False k a
+            newSize = (2+) . snd . bounds $ a
+        in if kc <= k
+            then newNode 0 newSize $
+                children p ++ [(kc, km), (k, value v)] ++ children n
+            else newNode 0 newSize $
+                children p ++ [(k, value v), (kc, km)] ++ children n
+    phi (Node d a) =
+        let (_, p, (_, (_, ka)), n) = lookupPos False k a
+            newSize = 1 + currSize
+            currSize = (1+) . snd . bounds $ a
+        in case ka of
+            Inserted k' n' -> newNode d currSize $
+                children p ++ (k', n'):children n
+            Split _ lt rt -> newNode d newSize $
+                children p ++ [lt, rt] ++ children n
+
+-- | 'Transaction' version of 'insertBTree'.
+insertBTreeT :: (Ord k, Binary k, Binary v) => k -> v ->
+    Transaction (Ref (BTree k v)) s ()
+insertBTreeT k v = alterT (insertBTree k v)
+
+-- | Lookup the values stored for the key 'k' in a 'Fixed' @('BTree' k v)@.
+lookupBTree :: (Ord k, Fixed g) => k -> g (BTree k v) -> [v]
+lookupBTree k = ($ []) . cata phi where
+    phi Empty l = l
+    phi (Value v) l = v:l
+    phi (Node 0 a) l = foldr ($) l . fmap snd . filter ((k ==) . fst) . elems
+        $ a
+    phi (Node _ a) l =
+        let (_, c, _) = splitRange k a
+        in foldr ($) l $ fmap snd c
+
+-- | 'Transaction' version of 'lookupBTree'.
+lookupBTreeT :: (Ord k, Binary k, Binary v) => k ->
+    Transaction (Ref (BTree k v)) s [v]
+lookupBTreeT k = lookupT (lookupBTree k)
+
+data Deleted k v g =
+    Deleted k (g (BTree k v))
+  | AllDeleted
+  | UnChanged
+
+-- | Filter items from a 'Fixed' @('BTree' k v)@ for a key 'k' that match
+--   the predicate.
+filterBTree :: (Ord k, Fixed g) => k -> (v -> Bool) ->
+    g (BTree k v) -> g (BTree k v)
+filterBTree k f t = deleted' . para phi $ t where
+    deleted' UnChanged = t
+    deleted' AllDeleted = empty
+    deleted' (Deleted _ x) = x
+    phi Empty = UnChanged
+    phi (Value v) = if f v
+        then UnChanged
+        else AllDeleted
+    phi (Node 0 a) =
+        let al = do
+                (nk, (nn, nv)) <- elems a
+                case (nk == k, nv) of
+                    (False, _) -> return (False, ((nk, nn):))
+                    (_, UnChanged) -> return (False, ((nk, nn):))
+                    _ -> return (True, id)
+            alb = foldr ((||) . fst) False al
+            al' = foldr (($) . snd) [] al
+            mink = fst . head $ al'
+        in case (alb, null al') of
+            (True, True) -> AllDeleted
+            (True, False) -> Deleted mink $ node 0 $
+                array (0, length al' - 1) $ zip [0..] al'
+            (False, _) -> UnChanged
+    phi (Node d a) = 
+        let (p, c, n) = splitRange k a
+            p' = [(nk, nv) | (nk, (nv, _)) <- p]
+            c'' = do
+                (nk, (nn, nv)) <- c
+                case nv of
+                    UnChanged -> return (False, ((nk, nn):))
+                    AllDeleted -> return (True, id)
+                    Deleted k' v' -> return (True, ((k', v'):))
+            c' = foldr (($) . snd) [] c''
+            cb = foldr ((||) . fst) False c''
+            n' = [(nk, nv) | (nk, (nv, _)) <- n]
+            al = p' ++ c' ++ n'
+            mink = fst . head $ al
+        in case (cb, null al) of
+            (False, _) -> UnChanged
+            (True, True) -> AllDeleted
+            (True, False) -> Deleted mink $ node d $
+                array (0, length al - 1) $ zip [0..] al
+
+-- | 'Transaction' version of 'filterBTree'.
+filterBTreeT :: (Ord k, Binary k, Binary v) => k -> (v -> Bool) ->
+    Transaction (Ref (BTree k v)) s ()
+filterBTreeT k f = alterT (filterBTree k f)
+
+-- | Delete all items for key 'k' from the 'Fixed' @('BTree' k v)@.
+deleteBTree :: (Ord k, Fixed g) => k -> g (BTree k v) -> g (BTree k v)
+deleteBTree k = filterBTree k (const False)
+
+-- | 'Transaction' version of 'deleteBTree'.
+deleteBTreeT :: (Ord k, Binary k, Binary v) => k ->
+    Transaction (Ref (BTree k v)) s ()
+deleteBTreeT k = alterT (deleteBTree k)
+
+-- | Turn a 'Fixed' @('BTree' k v)@ into a list of key value tuples.
+toListBTree :: (Ord k, Fixed g) => g (BTree k v) -> [(k,v)]
+toListBTree t = cata phi t Nothing [] where
+    phi Empty _ l = l
+    phi (Value v) (Just k) l = (k, v):l
+    phi (Value _) _ _ = error "Value with no Key"
+    phi (Node _ a) _ l = foldr (\(k,v) -> ((v (Just k)) .)) id (elems a) l
+
+-- | Turn a list of key value tuples into a 'Fixed' @('BTree' k v)@.
+fromListBTree :: (Ord k, Fixed g) => [(k,v)] -> g (BTree k v)
+fromListBTree = foldr (uncurry insertBTree) empty
+
diff --git a/src/Data/FixFile/Fixed.hs b/src/Data/FixFile/Fixed.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile/Fixed.hs
@@ -0,0 +1,97 @@
+
+{- |
+    Module      :  Data.FixFile.Fixed
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    This is a data type that can be used with a 'FixFile' to store a set of
+    'Ordered' items as an unbalanced binary tree. This file is not recommended
+    for use, but exists for educational purposes. It has a simple
+    implementation that is easier to read than some of the more advanced
+    balanced data types.
+-}
+module Data.FixFile.Fixed (
+                            Fix(..)
+                           ,Fixed(..)
+                           ,CataAlg
+                           ,cata
+                           ,AnaAlg
+                           ,ana
+                           ,ParaAlg
+                           ,para
+                           ,iso
+                           ) where
+
+{-|
+    'Fixed' is a typeclass for representing the fixed point of a 'Functor'.
+    A well-behaved instance of 'Fixed' should not change the shape of the
+    underlying 'Functor'.
+
+    In other words, the following should always be true:
+@
+'outf' ('inf' x) == x
+@
+ -}
+class Fixed g where
+    inf :: f (g f) -> g f
+    outf :: g f -> f (g f)
+
+{-|
+    'Fix' is a type for creating an in-memory representation of the fixed
+    point of a 'Functor'.
+
+-}
+newtype Fix f = InF { outF :: f (Fix f) }
+
+instance Fixed Fix where
+    inf = InF
+    {-# INLINE inf #-}
+    outf = outF
+    {-# INLINE outf #-}
+
+{-|
+    'AnaAlg' is an anamorpism F-Algebra.
+-}
+type AnaAlg f a = a -> f a
+
+{-|
+    'ana' applies an AnaAlg over an argument to produce a fixed-point
+    of a Functor.
+-}
+ana :: (Functor f, Fixed g) => AnaAlg f a -> a -> g f
+ana f = inf . fmap (ana f) . f
+
+{-|
+    'CataAlg' is a catamorphism F-Algebra.
+-}
+type CataAlg f a = f a -> a
+
+{-|
+    'cata' applies a 'CataAlg' over a fixed point of a 'Functor'.
+-}
+cata :: (Functor f, Fixed g) => CataAlg f a -> g f -> a
+cata f = f . fmap (cata f) . outf
+
+{-|
+    'ParaAlg' is a paramorphism F-Algebra.
+-}
+type ParaAlg g f a = f (g f, a) -> a
+
+{-|
+    'para' applies a 'ParaAlg' over a fixed point of a 'Functor'.
+-}
+para :: (Functor f, Fixed g) => ParaAlg g f a -> g f -> a
+para f = f . fmap para' . outf where
+    para' x = (x, para f x)
+
+{-|
+    'iso' maps from a fixed point of a 'Functor' to a different fixed
+    point of the same 'Functor'. For any two well-behaved instances of
+    'Fixed', the shape of the 'Functor' should remain unchanged.
+-}
+iso :: (Functor f, Fixed g, Fixed h) => g f -> h f
+iso = cata inf
+
diff --git a/src/Data/FixFile/Set.hs b/src/Data/FixFile/Set.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile/Set.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE DeriveGeneric, DeriveFunctor, DeriveFoldable, DeriveTraversable,
+    DeriveDataTypeable #-}
+
+{- |
+    Module      :  Data.FixFile.Set
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    This is a data type that can be used with a 'FixFile' to store a set of
+    'Ordered' items as an unbalanced binary tree. This file is not recommended
+    for use, but exists for educational purposes. It has a simple
+    implementation that is easier to read than some of the more advanced
+    balanced data types.
+-}
+module Data.FixFile.Set (Set
+                         ,createSetFile
+                         ,openSetFile
+                         ,empty
+                         ,insertSet
+                         ,insertSetT
+                         ,deleteSet
+                         ,deleteSetT
+                         ,lookupSet
+                         ,lookupSetT
+                         ,toListSet
+                         ,toListSetT
+                         ) where
+
+import Prelude hiding (lookup)
+
+import Data.Binary
+import Data.Dynamic
+import GHC.Generics
+
+import Data.FixFile
+{- |
+    A 'Fixed' @('Set' i)@ is a set of items represented as a binary tree.
+-}
+data Set i a = Empty | Node a i a
+    deriving (Read, Show, Generic, Functor, Foldable, Traversable, Typeable)
+
+instance (Binary i, Binary a) => Binary (Set i a)
+
+-- | An empty 'Set'.
+empty :: Fixed g => g (Set i)
+empty = inf Empty
+
+node :: Fixed g => g (Set i) -> i -> g (Set i) -> g (Set i)
+node l i r = inf $ Node l i r
+
+-- | Insert an item 'i' into a 'Fixed' recursive @'Set' i@.
+insertSet :: (Ord i, Fixed g) => i -> g (Set i) -> g (Set i)
+insertSet i s = newHead $ para phi s where
+    newHead = maybe s id
+    phi Empty = Just $ node empty i empty
+    phi (Node (ln, la) j (rn, ra)) = case compare i j of
+        EQ -> Nothing
+        LT -> la >>= \l -> return $ node l j rn
+        GT -> ra >>= \r -> return $ node ln j r
+
+-- | 'Transaction' version of 'insertSet'.
+insertSetT :: (Ord i, Binary i) => i -> Transaction (Ref (Set i)) s ()
+insertSetT i = alterT (insertSet i)
+
+-- | Delete an item 'i' into a 'Fixed' recursive @'Set' i@.
+deleteSet :: (Ord i, Fixed g) => i -> g (Set i) -> g (Set i)
+deleteSet i s = newHead $ para phi s Nothing where
+    newHead = maybe s id
+    phi Empty x = x
+    phi (Node (ln, la) j (rn, ra)) Nothing = case compare i j of
+        EQ -> la (Just rn)
+        LT -> la Nothing >>= \l -> return $ node l j rn
+        GT -> ra Nothing >>= \r -> return $ node ln j r
+    phi (Node (ln, _) j (_, ra)) x = node ln j <$> ra x
+
+-- | 'Transaction' version of 'deleteSet'.
+deleteSetT :: (Ord i, Binary i) => i -> Transaction (Ref (Set i)) s ()
+deleteSetT i = alterT (deleteSet i)
+
+-- | Predicate to lookup an item from a @'Set' i@.
+lookupSet :: (Ord i, Fixed g) => i -> g (Set i) -> Bool
+lookupSet i = cata phi where
+    phi Empty = False
+    phi (Node la j ra) = case compare i j of
+        EQ -> True
+        LT -> la
+        GT -> ra
+
+-- | 'FTransaction' version of 'lookupSet'.
+lookupSetT :: (Ord i, Binary i) => i -> Transaction (Ref (Set i)) s Bool
+lookupSetT i = lookupT (lookupSet i)
+
+-- | Create a @'FixFile' ('Set' i)@.
+createSetFile :: (Binary i, Typeable i) =>
+    FilePath -> IO (FixFile (Ref (Set i)))
+createSetFile fp = createFixFile (Ref empty) fp
+
+-- | Open a @'FixFile' ('Set' i)@.
+openSetFile :: (Binary i, Typeable i) =>
+    FilePath -> IO (FixFile (Ref (Set i)))
+openSetFile = openFixFile
+
+-- | Turn a 'Fixed' recurive structure of @'Set' i@ into a list.
+toListSet :: Fixed g => g (Set i) -> [i]
+toListSet s = cata phi s [] where
+    phi Empty l = l
+    phi (Node la i ra) l = (la . (i:) . ra) l
+
+-- | 'Transaction' version of 'toListSet'.
+toListSetT :: Binary i => Transaction (Ref (Set i)) s [i]
+toListSetT = lookupT toListSet
+
diff --git a/src/Data/FixFile/Tree23.hs b/src/Data/FixFile/Tree23.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile/Tree23.hs
@@ -0,0 +1,406 @@
+{-# LANGUAGE DeriveGeneric, DeriveFunctor, DeriveFoldable, DeriveTraversable,
+    KindSignatures, TypeFamilies, FlexibleInstances, FlexibleContexts,
+    DeriveDataTypeable #-}
+
+{- |
+    Module      :  Data.FixFile.Tree23
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    This is an implementation of a Two-Three Tree data structure that can be
+    used with 'FixFile'. It has two interfaces that are
+-}
+module Data.FixFile.Tree23 (Tree23
+                           ,TreeD
+                           ,empty
+                           ,null
+                           ,size
+                           -- | * Set
+                           ,Set
+                           ,createSetFile
+                           ,openSetFile
+                           ,insertSet
+                           ,lookupSet
+                           ,deleteSet
+                           ,toListSet
+                           ,fromListSet
+                           ,insertSetT
+                           ,lookupSetT
+                           ,deleteSetT
+                           -- | * Map
+                           ,Map
+                           ,createMapFile
+                           ,openMapFile
+                           ,insertMap
+                           ,lookupMap
+                           ,deleteMap
+                           ,alterMap
+                           ,mapMap
+                           ,toListMap
+                           ,fromListMap
+                           ,insertMapT
+                           ,lookupMapT
+                           ,deleteMapT
+                           ,alterMapT
+                           ,keysMap
+                           ,valuesMap
+                           ) where
+
+import Prelude hiding (null)
+
+import Data.Dynamic
+import Data.Binary
+import Data.Maybe
+import GHC.Generics
+ 
+import Data.FixFile
+
+data Tree23F k v a = 
+    Empty
+  | Leaf k v
+  | Two a k a
+  | Three a k a k a
+  deriving (Read, Show, Eq, Ord, Generic, Functor, Foldable, Traversable,
+            Typeable)
+
+{- |
+    'Fixed' @('TreeD' d)@ represents a Two-Three tree. The data type 'd' should
+    have data families for it's key and value. These data families are not
+    exported from the module. As a result, the only valid types for 'd' are
+    @('Set' k)@ as defined here or @('Map' k v)@, also defined here.
+-}
+type TreeD d = Tree23F (TreeKey d) (TreeValue d)
+
+-- | Type synonym for the 'Fixed' representation of a Two-Three Tree.
+type Tree23 g d = g (TreeD d)
+
+data family TreeKey d
+
+data family TreeValue d
+
+instance (Binary a, Binary (TreeKey d), Binary (TreeValue d)) =>
+    Binary (Tree23F (TreeKey d) (TreeValue d) a)
+
+-- | An empty 'Fixed' 'Tree23'.
+empty :: Fixed g => Tree23 g d
+empty = inf Empty
+
+leaf :: Fixed g => TreeKey d -> TreeValue d -> Tree23 g d
+leaf k v = inf $ Leaf k v
+
+two :: Fixed g => Tree23 g d -> TreeKey d ->
+    Tree23 g d -> Tree23 g d
+two l v r = inf $ Two l v r
+
+three :: Fixed g => Tree23 g d -> TreeKey d -> Tree23 g d ->
+    TreeKey d -> Tree23 g d -> Tree23 g d
+three l t1 m t2 r =
+    inf $ Three l t1 m t2 r
+
+-- | Predicate that returns true if there are no items in the 'Tree23'.
+null :: Fixed g => Tree23 g d -> Bool
+null = null' . outf where
+    null' Empty = True
+    null' _ = False
+
+-- | Number of entries in @('Tree23' g d)@.
+size :: Fixed g => Tree23 g d -> Int
+size = cata phi where
+    phi Empty = 0
+    phi (Leaf _ _) = 1
+    phi (Two l _ r) = l + r
+    phi (Three l _ m _ r) = l + m + r
+
+-- | A 'Set' of 'k' represented as a Two-Three Tree.
+data Set k
+
+newtype instance TreeKey (Set k) = SK k
+    deriving (Read, Show, Eq, Ord, Generic, Typeable)
+
+data instance TreeValue (Set k) = SV
+    deriving (Read, Show, Eq, Ord, Generic, Typeable)
+
+instance Binary k => Binary (TreeKey (Set k))
+
+instance Binary (TreeValue (Set k))
+
+-- | Insert an item into a set.
+insertSet :: (Fixed g, Ord k) => k -> Tree23 g (Set k) -> Tree23 g (Set k)
+insertSet k = alterTree23 (SK k) (maybe (Just $ Just SV) (const Nothing))
+
+-- | Lookup an item in a set.
+lookupSet :: (Fixed g, Ord k) => k -> Tree23 g (Set k) -> Bool
+lookupSet k = isJust . lookupTree23 (SK k)
+
+-- | Delete an item from a set.
+deleteSet :: (Fixed g, Ord k) => k -> Tree23 g (Set k) -> Tree23 g (Set k)
+deleteSet k = alterTree23 (SK k) (const $ Just Nothing)
+
+-- | Convert a set into a list of items.
+toListSet :: (Fixed g, Ord k) => Tree23 g (Set k) -> [k]
+toListSet = ($ []) . cata phi where
+    phi Empty xs = xs
+    phi (Leaf (SK k) _) xs = k:xs
+    phi (Two la _ ra) xs = la . ra $ xs
+    phi (Three la _ ma _ ra) xs = la . ma . ra $ xs
+
+-- | Convert a list of items into a set.
+fromListSet :: (Fixed g, Ord k) => [k] -> Tree23 g (Set k)
+fromListSet = Prelude.foldr insertSet empty
+
+-- | Create a 'FixFile' for storing a set of items.
+createSetFile :: (Binary k, Typeable k) =>
+    FilePath -> IO (FixFile (Ref (TreeD (Set k))))
+createSetFile fp = createFixFile (Ref empty) fp
+
+-- | Open a 'FixFile' for storing a set of items.
+openSetFile :: (Binary k, Typeable k) =>
+    FilePath ->IO (FixFile (Ref (TreeD (Set k))))
+openSetFile fp = openFixFile fp
+
+-- | 'Transaction' version of 'insertSet'.
+insertSetT :: (Binary k, Ord k) =>
+    k -> Transaction (Ref (TreeD (Set k))) s ()
+insertSetT k = alterT (insertSet k) 
+
+-- | 'FTransaction' version of 'lookupSet'.
+lookupSetT :: (Binary k, Ord k) =>
+    k -> Transaction (Ref (TreeD (Set k))) s Bool
+lookupSetT k = lookupT (lookupSet k)
+
+-- | 'FTransaction' version of 'deleteSet'.
+deleteSetT :: (Binary k, Ord k) =>
+    k -> Transaction (Ref (TreeD (Set k))) s ()
+deleteSetT k = alterT (deleteSet k)
+
+-- | A 'Map' of keys 'k' to values 'v' represented as a Two-Three Tree.
+data Map k v
+
+newtype instance TreeKey (Map k v) = MK k
+    deriving (Read, Show, Eq, Ord, Generic, Typeable)
+
+newtype instance TreeValue (Map k v) = MV { fromMV :: v }
+    deriving (Read, Show, Eq, Ord, Generic, Typeable)
+
+instance Binary k => Binary (TreeKey (Map k v))
+
+instance Binary v => Binary (TreeValue (Map k v))
+
+-- | Insert value 'v' into a map for key 'k'. Any existing value is replaced.
+insertMap :: (Fixed g, Ord k) => k -> v -> Tree23 g (Map k v) ->
+    Tree23 g (Map k v)
+insertMap k v = alterTree23 (MK k) (const . Just . Just $ MV v)
+
+-- | Lookup an item in a map corresponding to key 'k'.
+lookupMap :: (Fixed g, Ord k) => k -> Tree23 g (Map k v) -> Maybe v
+lookupMap k = fmap toV . lookupTree23 (MK k) where
+    toV (MV v) = v
+
+-- | Delete an item from a map at key 'k'.
+deleteMap :: (Fixed g, Ord k) => k -> Tree23 g (Map k v) -> Tree23 g (Map k v)
+deleteMap k = alterTree23 (MK k) (const . Just $ Nothing)
+
+-- | Apply a function to alter a Map at key 'k'. The function takes
+--   @('Maybe' v)@ as an argument for any possible exiting value and returns
+--   @Nothing@ to delete a value or @Just v@ to set a new value.
+alterMap :: (Fixed g, Ord k) => k -> (Maybe v -> Maybe v) ->
+    Tree23 g (Map k v) -> Tree23 g (Map k v)
+alterMap k f = alterTree23 (MK k) (Just . fmap MV . f . fmap fromMV)
+
+-- | Convert a map into a list of key-value tuples.
+toListMap :: (Fixed g, Ord k) => Tree23 g (Map k v) -> [(k,v)]
+toListMap = ($ []) . cata phi where
+    phi Empty xs = xs
+    phi (Leaf (MK k) (MV v)) xs = (k,v):xs
+    phi (Two la _ ra) xs = la . ra $ xs
+    phi (Three la _ ma _ ra) xs = la . ma . ra $ xs
+
+-- | Convert a lst of key-value tuples into a map.
+fromListMap :: (Fixed g, Ord k) => [(k,v)] -> Tree23 g (Map k v)
+fromListMap = Prelude.foldr (uncurry insertMap) empty
+
+-- | Return the list of keys in a map.
+keysMap :: (Fixed g, Ord k) => Tree23 g (Map k v) -> [k]
+keysMap = fmap fst . toListMap
+
+-- | Return a list of values in a map.
+valuesMap :: (Fixed g, Ord k) => Tree23 g (Map k v) -> [v]
+valuesMap = fmap snd . toListMap
+
+-- | Map a function over a map. Because of the way Tree23 is implemented, it is
+--   not possible to create a Functor instance to achieve this.
+mapMap :: (Fixed g, Fixed h, Ord k) => (a -> b) -> Tree23 g (Map k a) ->
+    Tree23 h (Map k b)
+mapMap f = cata phi where
+    phi Empty = empty
+    phi (Leaf (MK k) (MV a)) = leaf (MK k) (MV (f a))
+    phi (Two l (MK k) r) = two l (MK k) r
+    phi (Three l (MK k1) m (MK k2) r) = three l (MK k1) m (MK k2) r
+
+-- | Create a 'FixFile' of a Map.
+createMapFile :: (Binary k, Typeable k, Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (TreeD (Map k v))))
+createMapFile fp = createFixFile (Ref empty) fp
+
+-- | Open a 'FixFile' of a Map.
+openMapFile :: (Binary k, Typeable k, Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (TreeD (Map k v))))
+openMapFile fp = openFixFile fp
+
+-- | 'Transaction' version of 'insertMap'.
+insertMapT :: (Binary k, Binary v, Ord k) =>
+    k -> v -> Transaction (Ref (TreeD (Map k v))) s ()
+insertMapT k v = alterT (insertMap k v) 
+
+-- | 'Transaction' version of 'lookupMap'.
+lookupMapT :: (Binary k, Binary v, Ord k) =>
+    k -> Transaction (Ref (TreeD (Map k v))) s (Maybe v)
+lookupMapT k = lookupT (lookupMap k)
+
+-- | 'Transaction' version of 'deleteMap'.
+deleteMapT :: (Binary k, Binary v, Ord k) => k ->
+    Transaction (Ref (TreeD (Map k v))) s ()
+deleteMapT k = alterT (deleteMap k)
+
+-- | 'FTransaction' version of 'alterMap'.
+alterMapT :: (Binary k, Binary v, Ord k) => k ->
+    (Maybe v -> Maybe v) -> 
+    Transaction (Ref (TreeD (Map k v))) s ()
+alterMapT k f = alterT (alterMap k f)
+
+-- lookup the value (if it exists) from a Fixed Tree23 for a given key.
+lookupTree23 :: (Fixed g, Ord (TreeKey d)) => TreeKey d ->
+    Tree23 g d -> Maybe (TreeValue d)
+lookupTree23 k = cata phi where
+    phi Empty = Nothing
+    phi (Leaf k' v)
+        | k == k' = Just v
+        | otherwise = Nothing
+    phi (Two la k' ra) =
+        case compare k k' of
+            LT -> la
+            _ -> ra
+    phi (Three la k1 ma k2 ra) =
+        case (compare k k1, compare k k2) of
+            (LT, _) -> la
+            (_, LT) -> ma
+            (_, _) -> ra
+
+data Change g d =
+    NoChange
+  | Changed (Maybe (TreeKey d)) (Tree23 g d)
+  | Unbalanced (Maybe (TreeKey d)) (Tree23 g d)
+  | Hole
+  | Split (Tree23 g d) (TreeKey d) (Tree23 g d)
+
+-- So, this function is a bit overwhelming, but it does everything that to
+-- handle all of the operations that modify a 2-3 tree.
+--
+-- The (TreeKey d) is the key where the modification should take place.
+-- The function takes one argument which is Maybe the value stored in the
+-- tree for the given key.
+-- The function returns Nothing if no change is made to the tree, Just Nothing
+-- if the value should be deleted from the tree, and Just v for the new value]
+-- to be written to the tree.
+alterTree23 :: (Fixed g, Ord (TreeKey d)) => TreeKey d ->
+    (Maybe (TreeValue d) -> Maybe (Maybe (TreeValue d))) ->
+    Tree23 g d -> Tree23 g d
+alterTree23 k f t = processHead $ para phi t t where
+    processHead NoChange = t
+    processHead (Changed _ t') = t'
+    processHead Hole = empty
+    processHead (Unbalanced _ t') = t'
+    processHead (Split lt d rt) = two lt d rt
+
+    phi Empty _ = case f Nothing of
+        Just (Just v) -> Changed Nothing $ leaf k v
+        _ -> NoChange
+
+    phi (Leaf k' v') n
+        | k == k' = case f (Just v') of
+            Nothing -> NoChange
+            Just Nothing -> Hole
+            Just (Just v) -> Changed Nothing $ leaf k' v
+        | otherwise = case f Nothing of
+            Nothing -> NoChange
+            Just Nothing -> NoChange
+            Just (Just v) -> if k < k'
+                then Split (leaf k v) k' n
+                else Split n k (leaf k v)
+
+    phi (Two (ln, la) k' (rn, ra)) _
+        | k < k' = case la ln of
+            NoChange -> NoChange
+            Changed nk la' ->
+                Changed nk $ two la' k' rn
+            Split la' k'' ma'->
+                Changed Nothing $ three la' k'' ma' k' rn
+            Hole -> Unbalanced (Just k') rn
+            Unbalanced uk un -> case outf rn of
+                Three ln' k1 mn' k2 rn' -> Changed uk $
+                    two (two un k' ln') k1 (two mn' k2 rn')
+                Two ln' k1 rn' -> Unbalanced uk $
+                    three un k' ln' k1 rn'
+                _ -> error "Invalid Tree23"
+        | otherwise = case ra rn of
+            NoChange -> NoChange
+            Hole -> Unbalanced Nothing ln
+            Changed dk dn -> Changed Nothing $
+                two ln (maybe k' id dk) dn
+            Split ma' k'' ra' -> Changed Nothing $
+                three ln k' ma' k'' ra'
+            Unbalanced uk un -> case outf ln of
+                Three ln' k1 mn' k2 rn' -> Changed Nothing $
+                    two (two ln' k1 mn') k2 (two rn' (maybe k' id uk) un)
+                Two ln' k1 rn' -> Unbalanced Nothing $
+                    three ln' k1 rn' (maybe k' id uk) un
+                _ -> error "Invalid Tree23"
+
+    phi (Three (ln, la) k1 (mn, ma) k2 (rn, ra)) _ 
+        | k < k1 = case la ln of
+            NoChange -> NoChange
+            Hole -> Changed (Just k1) $ two mn k2 rn
+            Changed dk dn -> Changed dk $
+                three dn k1 mn k2 rn
+            Split ln' k' rn' -> Split
+                (two ln' k' rn') k1 (two mn k2 rn)
+            Unbalanced uk un -> case outf mn of
+                Three ln' k1' mn' k2' rn' -> Changed uk $
+                    three (two un k1 ln') k1' (two mn' k2' rn') k2 rn
+                Two ln' k1' rn' -> Changed uk $
+                    two (three un k1 ln' k1' rn') k2 rn
+                _ -> error "Invalid Tree23"
+
+        | k < k2 = case ma mn of
+            NoChange -> NoChange
+            Hole -> Changed Nothing $ two ln k2 rn
+            Changed dk dn -> Changed Nothing $
+                three ln (maybe k1 id dk) dn k2 rn
+            Split mn' k' rn' -> Split
+                (two ln k1 mn') k' (two rn' k2 rn)
+            Unbalanced uk un -> case outf rn of
+                Three ln' k1' mn' k2' rn' -> Changed Nothing $
+                    three ln (maybe k1 id uk) (two un k2 ln')
+                        k1' (two mn' k2' rn')
+                Two ln' k1' rn' -> Changed Nothing $
+                    two ln (maybe k1 id uk) (three un k2 ln' k1' rn')
+                _ -> error "Invalid Tree23"
+
+        | otherwise = case ra rn of
+            NoChange -> NoChange
+            Hole -> Changed Nothing $ two ln k1 mn
+            Changed dk dn -> Changed Nothing $
+                three ln k1 mn (maybe k2 id dk) dn
+            Split mn' k' rn' -> Split
+                (two ln k1 mn) k2 (two mn' k' rn')
+            Unbalanced uk un -> case outf mn of
+                Three ln' k1' mn' k2' rn' -> Changed Nothing $
+                    three ln k1 (two ln' k1' mn') k2'
+                        (two rn' (maybe k2 id uk) un)
+                Two ln' k1' rn' -> Changed Nothing $ two ln k1
+                    (three ln' k1' rn' (maybe k2 id uk) un)
+                _ -> error "Invalid Tree23"
+
diff --git a/src/Data/FixFile/Trie.hs b/src/Data/FixFile/Trie.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/FixFile/Trie.hs
@@ -0,0 +1,349 @@
+{-# LANGUAGE DeriveGeneric, DeriveFunctor, DeriveFoldable, DeriveTraversable,
+    TypeFamilies, DeriveDataTypeable #-}
+
+{- |
+    Module      :  Data.FixFile.Trie
+    Copyright   :  (C) 2016 Rev. Johnny Healey
+    License     :  LGPL-3
+    Maintainer  :  Rev. Johnny Healey <rev.null@gmail.com>
+    Stability   :  experimental
+    Portability :  unknown
+
+    This is a Trie data type that can be used with 'FixFile'. It can be used
+    as a key-value store where the key is a 'ByteString' of arbitrary size.
+-}
+module Data.FixFile.Trie (Trie
+                         ,empty
+                         ,freeze
+                         ,createTrieFile
+                         ,openTrieFile
+                         ,lookupTrie
+                         ,lookupTrieT
+                         ,insertTrie
+                         ,insertTrieT
+                         ,deleteTrie
+                         ,deleteTrieT
+                         ,iterateTrie
+                         ,iterateTrieT
+                         ,trieMap
+                         ) where
+
+import Prelude hiding (tail)
+
+import Control.Applicative hiding (empty)
+import Control.Monad
+import Data.Array
+import Data.Binary
+import qualified Data.ByteString.Lazy as BS
+import Data.Dynamic
+import qualified Data.Map as M
+import Data.Maybe
+import GHC.Generics
+
+import Data.FixFile
+
+-- | 'Fixed' @('Trie' v)@ is a trie mapping lazy 'ByteString's to values of
+--   type v.
+data Trie v a =
+    Value v
+  | Tail (Maybe a)
+  | String (Maybe a) BS.ByteString a
+  | Small (Maybe a) [(Word8, a)]
+  | Big (Maybe a) (Array Word8 (Maybe a))
+  | Mutable (Maybe a) (M.Map Word8 a)
+  deriving (Read, Show, Generic, Functor, Foldable, Traversable, Typeable)
+
+instance (Binary v, Binary a) => Binary (Trie v a) where
+    put (Value v) = putWord8 0 >> put v
+    put (Tail a) = putWord8 1 >> put a
+    put (String m b a) = putWord8 2 >> put m >> put b >> put a
+    put (Small m l) = putWord8 3 >> put m >> put l
+    put (Big m a) = putWord8 4 >> put m >> put a
+    put m = put $ freeze' m
+    get = getWord8 >>= getTrie where
+        getTrie 0 = Value <$> get
+        getTrie 1 = Tail <$> get
+        getTrie 2 = String <$> get <*> get <*> get
+        getTrie 3 = Small <$> get <*> get
+        getTrie 4 = Big <$> get <*> get
+        getTrie _ = error "Invalid Serialized Trie"
+
+value :: Fixed g => v -> g (Trie v)
+value = inf . Value
+
+tail :: Fixed g => Maybe (g (Trie v)) -> g (Trie v)
+tail = inf . Tail where
+
+-- | An empty 'Trie'
+empty :: Fixed g => g (Trie v)
+empty = inf $ Tail Nothing
+
+string :: Fixed g => Maybe (g (Trie v)) -> BS.ByteString ->
+    g (Trie v) -> g (Trie v)
+string v k t = inf $ String v k t
+
+fill :: Fixed g => BS.ByteString -> Maybe (g (Trie v)) -> g (Trie v) ->
+    g (Trie v)
+fill k x t = if BS.null k
+    then t
+    else string x k t
+
+small :: Fixed g => Maybe (g (Trie v)) -> [(Word8, g (Trie v))]
+    -> g (Trie v)
+small v l = inf $ Small v l
+
+big :: Fixed g => Maybe (g (Trie v)) ->
+    Array Word8 (Maybe (g (Trie v)))
+    -> g (Trie v)
+big v l = inf $ Big v l
+
+mut :: Fixed g => Maybe (g (Trie v)) -> M.Map Word8 (g (Trie v)) -> 
+    g (Trie v)
+mut v l = inf $ Mutable v l
+
+bigThreshold :: Int
+bigThreshold = 20
+
+-- | 'freeze' takes a 'Trie' that has been mutated and creates a copy of it
+-- that allows for faster lookups. This happens automatically for 'Trie's that
+-- are serialized to a 'FixFile'. A 'Trie' will be automatically thawed on
+-- any node that is modified.
+freeze :: Fixed g => g (Trie v) -> g (Trie v)
+freeze = cata (inf . freeze') where
+
+freeze' :: Trie v a -> Trie v a
+freeze' (Mutable a b) = if M.size b > bigThreshold
+    then Big a $ array (minBound, maxBound) $ do
+        i <- [minBound..maxBound]
+        case M.lookup i b of
+            Nothing -> return (i, Nothing)
+            Just t -> return (i, Just t)
+    else Small a $ M.toList b
+freeze' m = m
+
+thaw :: Trie v a -> Trie v a
+thaw (Big a b) = Mutable a . M.fromList $ do
+    (i, Just v) <- assocs b
+    return (i, v)
+thaw (Small a b) = Mutable a $ M.fromList b
+thaw m = m
+
+-- | Create a 'FixFile' of @('Trie' v)@ data.
+createTrieFile :: (Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (Trie v)))
+createTrieFile fp = createFixFile (Ref empty) fp
+
+-- | Open a 'FixFile' of @('Trie' v)@ data.
+openTrieFile :: (Binary v, Typeable v) =>
+    FilePath -> IO (FixFile (Ref (Trie v)))
+openTrieFile = openFixFile
+
+-- | Lookup a possible value stored in a trie for a given 'ByteString' key.
+lookupTrie :: Fixed g => BS.ByteString -> g (Trie v) -> Maybe v
+lookupTrie a b = cata phi b a where
+    term v k = guard (BS.null k) >> v >>= ($ k)
+    phi (Value v) _ = return v
+    phi (Tail v) k = term v k
+    phi (String v s t) k = term v k <|> do
+        let (_, lt, rt) = splitKey s k
+        guard (BS.null lt)
+        t rt
+    phi (Small v l) k = term v k <|> do
+        (c, r) <- BS.uncons k
+        t <- lookup c l
+        t r
+    phi (Big v l) k = term v k <|> do
+        (c, r) <- BS.uncons k
+        t <- l ! c
+        t r
+    phi (Mutable v l) k = term v k <|> do
+        (c, r) <- BS.uncons k
+        t <- M.lookup c l
+        t r
+
+-- | 'Transaction' version of 'lookupTrie'.
+lookupTrieT :: Binary v =>
+    BS.ByteString -> Transaction (Ref (Trie v)) s (Maybe v)
+lookupTrieT k = lookupT (lookupTrie k)
+
+splitKey :: BS.ByteString -> BS.ByteString ->
+    (BS.ByteString, BS.ByteString, BS.ByteString)
+splitKey x y = case (BS.uncons x, BS.uncons y) of
+    (Nothing, Nothing) -> (BS.empty, BS.empty, BS.empty)
+    (Nothing, Just _) -> (BS.empty, x, y)
+    (Just _, Nothing) -> (BS.empty, x, y)
+    (Just (xc, xs), Just (yc, ys)) -> if xc == yc
+        then let (shared, xt, yt) = splitKey xs ys
+            in (BS.cons xc shared, xt, yt)
+        else (BS.empty, x, y)
+
+-- | Insert a value into a trie for the given 'ByteString' key.
+insertTrie :: Fixed g => BS.ByteString -> v -> g (Trie v) -> g (Trie v)
+insertTrie a b c = para phi c a where
+    val = Just $ value b
+    valTail = tail val
+    phi (Value _) _ = error "Badly formed Trie"
+    phi (Tail vm) k = fill k (fmap fst vm) valTail
+    phi (String vm s (tn, ta)) k
+        | BS.null k = string val s tn
+        | otherwise =
+            let (sh, lt, rt) = splitKey k s
+                Just (lh, ls) = BS.uncons lt
+                Just (rh, rs) = BS.uncons rt
+                vm' = fmap fst vm
+            in case (BS.null sh, BS.null lt, BS.null rt) of
+                (True, False, False) -> mut vm' $ M.fromList
+                    [(lh, fill ls Nothing valTail), (rh, fill rs Nothing tn)]
+                (True, _, _) -> error "Invalid Key Split"
+                (_, False, False) -> string vm' sh $ mut Nothing $ M.fromList
+                    [(lh, fill ls Nothing valTail), (rh, fill rs Nothing tn)]
+                (_, True, False) -> string vm' sh $ string val rt tn
+                (_, False, True) -> string vm' sh $ ta lt
+                (_, True, True) -> string vm' s $ ta lt
+    phi x@(Big _ t) k
+        | BS.null k = big val (fmap (fmap fst) t)
+        | otherwise = phi (thaw x) k
+    phi x@(Small _ t) k
+        | BS.null k = small val (fmap (fmap fst) t)
+        | otherwise = phi (thaw x) k
+    phi (Mutable vm m) k = case BS.uncons k of
+        Nothing -> mut val (fmap fst m)
+        Just (kh, kt) -> mut (fmap fst vm) $ case M.lookup kh m of
+            Nothing -> M.insert kh (fill kt Nothing valTail) $ fmap fst m
+            Just (_, ta) -> M.insert kh (ta kt) $ fmap fst m
+
+-- | 'Transaction' version of 'insertTrie'.
+insertTrieT :: Binary v =>
+    BS.ByteString -> v -> Transaction (Ref (Trie v)) s ()
+insertTrieT k v = alterT (insertTrie k v)
+ 
+data Deleted g v = 
+    NoDelete
+  | Deleted Bool (g (Trie v)) (Maybe (BS.ByteString, g (Trie v)))
+
+-- | Delete a value from a trie for a given 'ByteString' key.
+deleteTrie :: Fixed g => BS.ByteString -> g (Trie v) -> g (Trie v)
+deleteTrie a b = newHead $ para phi b a where
+    newHead NoDelete = b
+    newHead (Deleted True _ _) = empty
+    newHead (Deleted _ h _) = h
+    phi (Value _) _ = error "Invalid Trie"
+    phi (Tail _) k = if BS.null k
+        then Deleted True empty Nothing
+        else NoDelete
+    phi (String vm s (tn, ta)) k
+        | BS.null k = if isJust vm
+            then Deleted False (string Nothing s tn) (Just (s, tn))
+            else NoDelete
+    
+        | otherwise =
+            let (_, lt, rt) = splitKey k s
+                ta' = ta lt
+                vm' = fmap fst vm
+            in if BS.null rt
+                then case ta' of
+                    NoDelete -> NoDelete
+                    Deleted True _ _ -> if isNothing vm'
+                        then Deleted True empty Nothing
+                        else Deleted False (tail vm') Nothing
+                    Deleted False tn' (Just (b', tn'')) ->
+                        Deleted False (string vm' s tn') $
+                            Just (BS.append s b', tn'')
+                    Deleted False tn' Nothing ->
+                        Deleted False (string vm' s tn') Nothing
+                else NoDelete
+    phi x@(Small vm ts) k
+        | BS.null k = case vm of
+            Nothing -> NoDelete
+            _ -> Deleted False (small Nothing (fmap (fmap fst) ts)) Nothing
+        | otherwise = phi (thaw x) k
+    phi x@(Big vm ts) k
+        | BS.null k = case vm of
+            Nothing -> NoDelete
+            _ -> Deleted False (big Nothing (fmap (fmap fst) ts)) Nothing
+        | otherwise = phi (thaw x) k
+    phi (Mutable vm ts) k
+        | BS.null k = case vm of
+            Nothing -> NoDelete
+            _ -> Deleted False (mut Nothing (fmap fst ts)) Nothing
+        | otherwise = fromJust . (<|> Just NoDelete) $ do
+            (kh, kt) <- BS.uncons k
+            (_, ta) <- M.lookup kh ts
+            return $ case ta kt of
+                Deleted True _ _ ->
+                    let ts' = fmap fst $ M.delete kh $ ts
+                        mut' = mut vm' ts'
+                        vm' = fmap fst vm
+                    in case M.size ts' of
+                        0 -> if isNothing vm'
+                            then Deleted True empty Nothing
+                            else Deleted False (tail vm') Nothing
+                        _ -> Deleted False mut' Nothing
+                Deleted False dt _ ->
+                    let ts' = M.insert kh dt $ fmap fst ts
+                        mut' = mut vm' ts'
+                        vm' = fmap fst vm
+                    in Deleted False mut' Nothing
+                _ -> NoDelete
+
+-- | 'Transaction' version of 'deleteTrie'.
+deleteTrieT :: Binary v =>
+    BS.ByteString -> Transaction (Ref (Trie v)) s ()
+deleteTrieT k = alterT (deleteTrie k)
+
+-- | Iterate over a Trie for all of the 'ByteString' and value tuples for a
+-- given 'ByteString' prefix.
+iterateTrie :: Fixed g => BS.ByteString -> g (Trie v) -> [(BS.ByteString, v)]
+iterateTrie a b = cata phi b a BS.empty [] where
+    phi (Value v) _ k' l = (k',v):l
+    phi (Tail vm) k k' l = maybe l (\va -> va k k' l) (guard (BS.null k) >> vm)
+    phi (String vm s ta) k k' l =
+        let (_, lt, rt) = splitKey k s
+            f l' = maybe l' (\va -> va k k' l') (guard (BS.null k) >> vm)
+        in if BS.null lt || BS.null rt
+            then f $ ta lt (BS.append k' s) l 
+            else f l
+    phi (Small vm ts) k k' l  = 
+        let f l' = maybe l' (\va -> va k k' l') (guard (BS.null k) >> vm)
+        in case BS.uncons k of
+            Nothing -> ($ l) . (f .) . Prelude.foldr (.) id $ do
+                (i, r) <- ts
+                return $ r BS.empty (BS.snoc k' i)
+            Just (i, k'') -> case lookup i ts of
+                Nothing -> l
+                Just r -> r k'' (BS.snoc k' i) l
+    phi (Big vm ts) k k' l  = 
+        let f l' = maybe l' (\va -> va k k' l') (guard (BS.null k) >> vm)
+        in case BS.uncons k of
+            Nothing -> ($ l) . (f .) . Prelude.foldr (.) id $ do
+                (i, Just r) <- assocs ts
+                return $ r BS.empty (BS.snoc k' i)
+            Just (i, k'') -> case ts ! i of
+                Nothing -> l
+                Just r -> r k'' (BS.snoc k' i) l
+    phi (Mutable vm ts) k k' l  = 
+        let f l' = maybe l' (\va -> va k k' l') (guard (BS.null k) >> vm)
+        in case BS.uncons k of
+            Nothing -> ($ l) . (f .) . Prelude.foldr (.) id $ do
+                (i, r) <- M.toList ts
+                return $ r BS.empty (BS.snoc k' i)
+            Just (i, k'') -> case M.lookup i ts of
+                Nothing -> l
+                Just r -> r k'' (BS.snoc k' i) l
+
+-- | 'Transaction' version of 'iterateTrie'.
+iterateTrieT :: Binary v => BS.ByteString ->
+    Transaction (Ref (Trie v)) s [(BS.ByteString, v)]
+iterateTrieT k = lookupT (iterateTrie k)
+
+-- | Map a function over a 'Fixed' 'Trie'. Because of the data types used,
+--   this can't be implemented as a 'Functor'.
+trieMap :: (Fixed h, Fixed i) => (v -> v') -> h (Trie v) -> i (Trie v')
+trieMap f = cata phi where
+    phi (Value v) = value (f v)
+    phi (Tail v) = tail v
+    phi (String v b t) = string v b t
+    phi (Small v ts) = small v ts
+    phi (Big v ts) = big v ts
+    phi (Mutable v ts) = mut v ts
+
diff --git a/tests/Tests.hs b/tests/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Tests.hs
@@ -0,0 +1,20 @@
+
+import Test.Tasty
+
+import TestSet
+import TestTree23
+import TestTrie
+import TestBTree
+import TestFixFile
+
+main :: IO ()
+main = defaultMain $
+    testGroup "FixFile"
+    [
+        test23
+       ,testSet
+       ,testTrie
+       ,testBTree
+       ,testFixFile
+    ]
+
