diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/executables/APITests.hs b/executables/APITests.hs
deleted file mode 100644
--- a/executables/APITests.hs
+++ /dev/null
@@ -1,9 +0,0 @@
-{-# OPTIONS_GHC -F -pgmF htfpp #-}
-
-import Test.Framework
-import BasePrelude
-
-import {-@ HTF_TESTS @-} APITests.MapTests
-
-
-main = htfMain $ htf_thisModulesTests : htf_importedTests
diff --git a/executables/ConcurrentInsertionBench.hs b/executables/ConcurrentInsertionBench.hs
deleted file mode 100644
--- a/executables/ConcurrentInsertionBench.hs
+++ /dev/null
@@ -1,131 +0,0 @@
-
-import STMContainers.Prelude
-import Criterion.Main
-import Control.Monad.Free
-import Control.Monad.Free.TH
-import qualified Data.HashMap.Strict as UC
-import qualified STMContainers.Map as SC
-import qualified Control.Concurrent.Async as Async
-import qualified System.Random.MWC.Monad as MWC
-import qualified Focus
-import qualified Data.Char as Char
-import qualified Data.Text as Text
-
-
-type UCMap k v = TVar (UC.HashMap k (TVar v))
-
-
--- * Transactions 
--------------------------
-
-data TransactionF k v n where
-  Insert :: v -> k -> n -> TransactionF k v n
-  deriving (Functor)
-
-type Transaction k v = Free (TransactionF k v)
-
-
--- * Interpreters
--------------------------
-
-type Interpreter m = 
-  forall k v r. (Hashable k, Eq k) => m k v -> Transaction k v r -> STM r
-
-ucInterpreter :: Interpreter UCMap
-ucInterpreter m = 
-  iterM $ \case
-    Insert v k n -> do
-      mv <- readTVar m
-      vt <- newTVar v
-      writeTVar m $! UC.insert k vt mv
-      n
-
-specializedSCInterpreter :: Interpreter SC.Map
-specializedSCInterpreter m =
-  iterM $ \case
-    Insert v k n -> SC.insert v k m >> n
-
-focusSCInterpreter :: Interpreter SC.Map
-focusSCInterpreter m =
-  iterM $ \case
-    Insert v k n -> SC.focus (Focus.insertM v) k m >> n
-
-
--- * Session and runners
--------------------------
-
--- | A list of transactions per thread.
-type Session k v = [[Transaction k v ()]]
-
-type SessionRunner = 
-  forall k v. (Hashable k, Eq k) => Session k v -> IO ()
-
-scSessionRunner :: Interpreter SC.Map -> SessionRunner
-scSessionRunner interpreter threadTransactions = do
-  m <- atomically $ SC.new
-  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
-    forM_ actions $ atomically . interpreter m
-
-ucSessionRunner :: SessionRunner
-ucSessionRunner threadTransactions = do
-  m <- newTVarIO UC.empty
-  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
-    forM_ actions $ atomically . ucInterpreter m
-
-
--- * Generators
--------------------------
-
-type Generator a = MWC.Rand IO a
-
-transactionGenerator :: Generator (Transaction Text.Text () ())
-transactionGenerator = do
-  k <- keyGenerator
-  return $ Free $ Insert () k (Pure ())
-
-keyGenerator :: Generator Text.Text
-keyGenerator = do
-  l <- length
-  s <- replicateM l char
-  return $! Text.pack s
-  where
-    length = MWC.uniformR (7, 20)
-    char = Char.chr <$> MWC.uniformR (Char.ord 'a', Char.ord 'z')
-
-
--- * Utils
--------------------------
-
-slices :: Int -> [a] -> [[a]]
-slices size l =
-  case splitAt size l of
-    ([], _) -> []
-    (a, b) -> a : slices size b
-
-
--- * Main
--------------------------
-
-main = do
-  allTransactions <- MWC.runWithCreate $ replicateM actionsNum transactionGenerator
-  defaultMain $! flip map threadsNums $! \threadsNum ->
-    let
-      sliceSize = actionsNum `div` threadsNum
-      threadTransactions = slices sliceSize allTransactions
-      in 
-        bgroup
-          (shows threadsNum . showString "/" . shows sliceSize $ "")
-          [
-            bgroup "STM Containers"
-              [
-                bench "Focus-based" $ 
-                  scSessionRunner focusSCInterpreter threadTransactions,
-                bench "Specialized" $ 
-                  scSessionRunner specializedSCInterpreter threadTransactions
-              ],
-            bench "Unordered Containers" $
-              ucSessionRunner threadTransactions
-          ]
-  where
-    actionsNum = 100000
-    threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 40, 52, 64, 80, 128]
diff --git a/executables/ConcurrentTransactionsBench.hs b/executables/ConcurrentTransactionsBench.hs
deleted file mode 100644
--- a/executables/ConcurrentTransactionsBench.hs
+++ /dev/null
@@ -1,208 +0,0 @@
-
-import STMContainers.Prelude
-import STMContainers.Transformers
-import Criterion.Main
-import Control.Monad.Free
-import Control.Monad.Free.TH
-import qualified Data.HashMap.Strict as UC
-import qualified STMContainers.Map as SC
-import qualified Control.Concurrent.Async as Async
-import qualified System.Random.MWC.Monad as MWC
-import qualified Focus
-import qualified Data.Char as Char
-import qualified Data.Text as Text
-import qualified Data.Set as Set
-
-
--- * Custom data structures
--------------------------
-
-type UCMap k v = TVar (UC.HashMap k (TVar v))
-
-
--- * Transactions 
--------------------------
-
-data TransactionF k v n where
-  Insert :: v -> k -> n -> TransactionF k v n
-  Delete :: k -> n -> TransactionF k v n
-  Lookup :: k -> n -> TransactionF k v n
-  deriving (Functor, Show)
-
-type Transaction k v = Free (TransactionF k v)
-
--- * Interpreters
--------------------------
-
-type Interpreter m = 
-  forall k v r. (Hashable k, Eq k) => m k v -> Transaction k v r -> STM r
-
-ucInterpreter :: Interpreter UCMap
-ucInterpreter m = 
-  iterM $ \case
-    Insert v k c -> 
-      do
-        mv <- readTVar m
-        vt <- newTVar v
-        writeTVar m $! UC.insert k vt mv
-        c
-    Delete k c -> 
-      readTVar m >>= writeTVar m . UC.delete k >> c
-    Lookup k c -> 
-      readTVar m >>= mapM readTVar . UC.lookup k >> c
-
-specializedSCInterpreter :: Interpreter SC.Map
-specializedSCInterpreter m =
-  iterM $ \case
-    Insert v k c -> SC.insert v k m >> c
-    Delete k c   -> SC.delete k m >> c
-    Lookup k c   -> SC.lookup k m >> c
-
-focusSCInterpreter :: Interpreter SC.Map
-focusSCInterpreter m =
-  iterM $ \case
-    Insert v k c -> SC.focus (Focus.insertM v) k m >> c
-    Delete k c   -> SC.focus Focus.deleteM k m >> c
-    Lookup k c   -> SC.focus Focus.lookupM k m >> c
-
-
--- * Session and runners
--------------------------
-
--- | A list of transactions per thread.
-type Session k v = [[Transaction k v ()]]
-
-type SessionRunner = 
-  forall k v. (Hashable k, Eq k) => Session k v -> IO ()
-
-scSessionRunner :: Interpreter SC.Map -> SessionRunner
-scSessionRunner interpreter threadTransactions = do
-  m <- atomically $ SC.new
-  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
-    forM_ actions $ atomically . interpreter m
-
-ucSessionRunner :: SessionRunner
-ucSessionRunner threadTransactions = do
-  m <- newTVarIO UC.empty
-  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
-    forM_ actions $ atomically . ucInterpreter m
-
-
--- * Generators
--------------------------
-
-type Generator a = MWC.Rand IO a
-
--- |
--- Generate a list of transactions with a context of shared keys.
-transactionsGroupGenerator :: Int -> Generator [Transaction Text.Text () ()]
-transactionsGroupGenerator n =
-  (flip evalStateT) Set.empty $ replicateM n transaction
-  where
-    transaction = do
-      s <- size
-      fmap sequence_ $ replicateM s action
-      where
-        size = 
-          lift $ join $ weightedElementGenerator $
-          [
-            (1  , MWC.uniformR (8, 14)),
-            (5  , MWC.uniformR (2, 7)),
-            (10 , return 1)
-          ]
-        action =
-          join $ lift $ weightedElementGenerator $
-          [
-            (1, delete),
-            (5, insert),
-            (20, lookup)
-          ]
-          where
-            insert = do
-              k <- key 10 1
-              modify $ Set.insert k
-              return $ liftF (Insert () k ())
-            delete = do
-              k <- key 1 10
-              modify $ Set.delete k
-              return $ liftF (Delete k ())
-            lookup = do
-              k <- key 1 5
-              return $ liftF (Lookup k ())
-            key unknownWeight knownWeight =
-              join $ lift $ weightedElementGenerator $ 
-              [
-                (unknownWeight, unknown), 
-                (knownWeight, known)
-              ]
-              where
-                unknown = lift $ keyGenerator
-                known = do
-                  allKeys <- get
-                  maybe unknown return =<< lift (setElementGenerator allKeys)
-              
-keyGenerator :: Generator Text.Text
-keyGenerator = do
-  l <- length
-  s <- replicateM l char
-  return $! Text.pack s
-  where
-    length = MWC.uniformR (7, 20)
-    char = Char.chr <$> MWC.uniformR (Char.ord 'a', Char.ord 'z')
-
-setElementGenerator :: Set.Set a -> Generator (Maybe a)
-setElementGenerator set = do
-  case Set.size set of
-    0 -> return Nothing
-    size -> Just . (flip Set.elemAt) set <$> MWC.uniformR (0, pred size)
-
-elementGenerator :: [a] -> Generator a
-elementGenerator = \case
-  [] -> $bug "Empty list"
-  l -> (!!) l <$> MWC.uniformR (0, (pred . length) l)
-
-weightedElementGenerator :: [(Int, a)] -> Generator a
-weightedElementGenerator = \case
-  [] -> 
-    $bug "Empty list"
-  l -> 
-    (flip pick) l <$> MWC.uniformR (1, total)
-    where
-      total = (sum . map fst) l
-      pick n = \case
-        (n', e) : t ->
-          if n' >= n
-            then e 
-            else pick (n - n') t
-
--- * Utils
--------------------------
-
-slices :: Int -> [a] -> [[a]]
-slices size l =
-  case splitAt size l of
-    ([], _) -> []
-    (a, b) -> a : slices size b
-
-
--- * Main
--------------------------
-
-main = do
-  -- Pregenerate the transactions:
-  transactionsGroups <- 
-    MWC.runWithCreate $ replicateM (maximum threadsNums) $
-    transactionsGroupGenerator (transactionsNum `div` (maximum threadsNums))
-
-  -- Run the benchmark:
-  defaultMain $! flip map threadsNums $ \threadsNum -> 
-    let
-      session = 
-        map concat $! 
-        slices (length transactionsGroups `div` threadsNum) transactionsGroups
-      in
-        bench (shows threadsNum . showString "/" . shows (transactionsNum `div` threadsNum) $ "") $
-          scSessionRunner specializedSCInterpreter session
-  where
-    threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 40, 52, 64, 80, 128]
-    transactionsNum = 400000
diff --git a/executables/InsertionBench.hs b/executables/InsertionBench.hs
deleted file mode 100644
--- a/executables/InsertionBench.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-
-import STMContainers.Prelude
-import Criterion.Main
-import qualified Data.HashTable.IO as Hashtables
-import qualified Data.HashMap.Strict as UnorderedContainers
-import qualified Data.Map as Containers
-import qualified STMContainers.Map as STMContainers
-import qualified Focus
-import qualified System.Random.MWC.Monad as MWC
-import qualified Data.Char as Char
-import qualified Data.Text as Text
-
-main = do
-  keys <- MWC.runWithCreate $ replicateM rows keyGenerator
-  defaultMain
-    [
-      bgroup "STM Containers"
-      [
-        bench "focus-based" $ 
-          do
-            t <- atomically $ STMContainers.new :: IO (STMContainers.Map Text.Text ())
-            forM_ keys $ \k -> atomically $ STMContainers.focus (Focus.insertM ()) k t
-        ,
-        bench "specialized" $
-          do
-            t <- atomically $ STMContainers.new :: IO (STMContainers.Map Text.Text ())
-            forM_ keys $ \k -> atomically $ STMContainers.insert () k t
-      ]
-      ,
-      bench "Unordered Containers" $
-        nf (foldr (\k -> UnorderedContainers.insert k ()) UnorderedContainers.empty) keys
-      ,
-      bench "Containers" $
-        nf (foldr (\k -> Containers.insert k ()) Containers.empty) keys
-      ,
-      bench "Hashtables" $ 
-        do
-          t <- Hashtables.new :: IO (Hashtables.BasicHashTable Text.Text ())
-          forM_ keys $ \k -> Hashtables.insert t k ()
-    ]
-
-rows :: Int = 100000
-
-keyGenerator :: MWC.Rand IO Text.Text
-keyGenerator = do
-  l <- length
-  s <- replicateM l char
-  return $! Text.pack s
-  where
-    length = MWC.uniformR (7, 20)
-    char = Char.chr <$> MWC.uniformR (Char.ord 'a', Char.ord 'z')
-
diff --git a/executables/WordArrayTests.hs b/executables/WordArrayTests.hs
deleted file mode 100644
--- a/executables/WordArrayTests.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-{-# OPTIONS_GHC -F -pgmF htfpp #-}
-
-import Test.Framework
-import STMContainers.Prelude
-import STMContainers.Transformers
-import qualified STMContainers.WordArray as WordArray
-import qualified Focus
-import qualified WordArrayTests.Update as Update
-
-
-main = htfMain $ htf_thisModulesTests
-
-prop_differentInterpretersProduceSameResults (update :: Update.Update Char ()) =
-  Update.interpretMaybeList update ==
-  fmap WordArray.toMaybeList (Update.interpretWordArray update)
-
-prop_fromListIsIsomorphicToToList =
-  forAll gen prop
-  where
-    gen = do
-      indices <- (nub . sort) <$> listOf index
-      mapM (liftA2 (flip (,)) char . pure) indices
-      where
-        index = choose (0, pred (WordArray.maxSize)) :: Gen Int
-        char = arbitrary :: Gen Char
-    prop list = 
-      list === (WordArray.toList . WordArray.fromList) list
-
-test_focusMLookup = do
-  assertEqual (Just 'a') . fst =<< WordArray.focusM Focus.lookupM 3 w
-  assertEqual (Just 'b') . fst =<< WordArray.focusM Focus.lookupM 7 w
-  assertEqual Nothing . fst =<< WordArray.focusM Focus.lookupM 1 w
-  assertEqual Nothing . fst =<< WordArray.focusM Focus.lookupM 11 w
-  where
-    w = WordArray.fromList [(3, 'a'), (7, 'b'), (14, 'c')]
-
-test_foldable = do
-  assertEqual "abdc" $ toList w
-  where
-    w = WordArray.fromList [(3, 'a'), (7, 'b'), (8, 'd'), (14, 'c')]
diff --git a/library/STMContainers/Bimap.hs b/library/STMContainers/Bimap.hs
deleted file mode 100644
--- a/library/STMContainers/Bimap.hs
+++ /dev/null
@@ -1,143 +0,0 @@
-module STMContainers.Bimap
-(
-  Bimap,
-  Association,
-  new,
-  newIO,
-  insert1,
-  insert2,
-  delete1,
-  delete2,
-  lookup1,
-  lookup2,
-  focus1,
-  focus2,
-  foldM,
-  null,
-)
-where
-
-import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)
-import qualified Focus
-import qualified STMContainers.Map as Map
-
-
--- |
--- A bidirectional map.
--- Essentially a bijection between subsets of its two argument types.
--- 
--- For one value of a left-hand type this map contains one value 
--- of the right-hand type and vice versa.
-data Bimap a b = 
-  Bimap {m1 :: !(Map.Map a b), m2 :: !(Map.Map b a)}
-
--- |
--- A standard constraint for associations.
-type Association a b = (Map.Key a, Map.Key b)
-
--- |
--- Construct a new bimap.
-{-# INLINABLE new #-}
-new :: STM (Bimap a b)
-new = Bimap <$> Map.new <*> Map.new
-
--- |
--- Construct a new bimap in IO.
--- 
--- This is useful for creating it on a top-level using 'unsafePerformIO', 
--- because using 'atomically' inside 'unsafePerformIO' isn't possible.
-{-# INLINABLE newIO #-}
-newIO :: IO (Bimap a b)
-newIO = Bimap <$> Map.newIO <*> Map.newIO
-
--- |
--- Check on being empty.
-{-# INLINABLE null #-}
-null :: Bimap a b -> STM Bool
-null = Map.null . m1
-
--- |
--- Look up a right value by a left value.
-{-# INLINABLE lookup1 #-}
-lookup1 :: (Association a b) => a -> Bimap a b -> STM (Maybe b)
-lookup1 k = Map.lookup k . m1
-
--- |
--- Look up a left value by a right value.
-{-# INLINABLE lookup2 #-}
-lookup2 :: (Association a b) => b -> Bimap a b -> STM (Maybe a)
-lookup2 k = Map.lookup k . m2
-
--- |
--- Insert an association by a left value.
-{-# INLINABLE insert1 #-}
-insert1 :: (Association a b) => b -> a -> Bimap a b -> STM ()
-insert1 b a (Bimap m1 m2) = 
-  do
-    Map.insert b a m1
-    Map.insert a b m2
-
--- |
--- Insert an association by a right value.
-{-# INLINABLE insert2 #-}
-insert2 :: (Association a b) => a -> b -> Bimap a b -> STM ()
-insert2 b a (Bimap m1 m2) = (inline insert1) b a (Bimap m2 m1)
-
--- |
--- Delete an association by a left value.
-{-# INLINABLE delete1 #-}
-delete1 :: (Association a b) => a -> Bimap a b -> STM ()
-delete1 k (Bimap m1 m2) =
-  Map.focus lookupAndDeleteStrategy k m1 >>= 
-    mapM_ (\k' -> Map.delete k' m2)
-  where
-    lookupAndDeleteStrategy r =
-      return (r, Focus.Remove)
-
--- |
--- Delete an association by a right value.
-{-# INLINABLE delete2 #-}
-delete2 :: (Association a b) => b -> Bimap a b -> STM ()
-delete2 k (Bimap m1 m2) = (inline delete1) k (Bimap m2 m1)
-
--- |
--- Focus on a right value by a left value with a strategy.
--- 
--- This function allows to perform composite operations in a single access
--- to a map item.
--- E.g., you can look up an item and delete it at the same time,
--- or update it and return the new value.
-{-# INLINABLE focus1 #-}
-focus1 :: (Association a b) => Focus.StrategyM STM b r -> a -> Bimap a b -> STM r
-focus1 s a (Bimap m1 m2) =
-  do 
-    (r, d, mb) <- Map.focus s' a m1
-    case d of
-      Focus.Keep -> 
-        return ()
-      Focus.Remove -> 
-        forM_ mb $ \b -> Map.delete b m2
-      Focus.Replace b' ->
-        do
-          forM_ mb $ \b -> Map.delete b m2
-          Map.insert a b' m2
-    return r
-  where
-    s' = \k -> s k >>= \(r, d) -> return ((r, d, k), d)
-
--- |
--- Focus on a left value by a right value with a strategy.
--- 
--- This function allows to perform composite operations in a single access
--- to a map item.
--- E.g., you can look up an item and delete it at the same time,
--- or update it and return the new value.
-{-# INLINABLE focus2 #-}
-focus2 :: (Association a b) => Focus.StrategyM STM a r -> b -> Bimap a b -> STM r
-focus2 s b (Bimap m1 m2) = (inline focus1) s b (Bimap m2 m1)
-
--- |
--- Fold all the associations.
-{-# INLINABLE foldM #-}
-foldM :: (r -> (a, b) -> STM r) -> r -> Bimap a b -> STM r
-foldM s r = Map.foldM s r . m1
diff --git a/library/STMContainers/HAMT.hs b/library/STMContainers/HAMT.hs
deleted file mode 100644
--- a/library/STMContainers/HAMT.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-module STMContainers.HAMT where
-
-import STMContainers.Prelude hiding (insert, lookup, delete, foldM)
-import qualified STMContainers.HAMT.Nodes as Nodes
-import qualified Focus
-
-
-type HAMT e = Nodes.Nodes e
-
-type Element e = (Nodes.Element e, Hashable (Nodes.ElementKey e))
-
-{-# INLINE insert #-}
-insert :: (Element e) => e -> HAMT e -> STM ()
-insert e = Nodes.insert e (hash (Nodes.elementKey e)) (Nodes.elementKey e) 0
-
-{-# INLINE focus #-}
-focus :: (Element e) => Focus.StrategyM STM e r -> Nodes.ElementKey e -> HAMT e -> STM r
-focus s k = Nodes.focus s (hash k) k 0
-
-{-# INLINE foldM #-}
-foldM :: (a -> e -> STM a) -> a -> HAMT e -> STM a
-foldM step acc = Nodes.foldM step acc 0
-
-{-# INLINE new #-}
-new :: STM (HAMT e)
-new = Nodes.new
-
-{-# INLINE newIO #-}
-newIO :: IO (HAMT e)
-newIO = Nodes.newIO
-
-{-# INLINE null #-}
-null :: HAMT e -> STM Bool
-null = Nodes.null
diff --git a/library/STMContainers/HAMT/Level.hs b/library/STMContainers/HAMT/Level.hs
deleted file mode 100644
--- a/library/STMContainers/HAMT/Level.hs
+++ /dev/null
@@ -1,29 +0,0 @@
-module STMContainers.HAMT.Level where
-
-import STMContainers.Prelude hiding (mask)
-
-
--- |
--- A depth level of a node.
--- Must be a multiple of the 'step' value.
-type Level = Int
-
-{-# INLINE hashIndex #-}
-hashIndex :: Level -> (Int -> Int)
-hashIndex l i = mask .&. unsafeShiftR i l
-
-{-# INLINE mask #-}
-mask :: Int
-mask = bit step - 1
-
-{-# INLINE step #-}
-step :: Int
-step = 5
-
-{-# INLINE limit #-}
-limit :: Int
-limit = bitSize (undefined :: Int)
-
-{-# INLINE succ #-}
-succ :: Level -> Level
-succ = (+ step)
diff --git a/library/STMContainers/HAMT/Nodes.hs b/library/STMContainers/HAMT/Nodes.hs
deleted file mode 100644
--- a/library/STMContainers/HAMT/Nodes.hs
+++ /dev/null
@@ -1,153 +0,0 @@
-module STMContainers.HAMT.Nodes where
-
-import STMContainers.Prelude hiding (insert, lookup, delete, foldM, null)
-import qualified STMContainers.Prelude as Prelude
-import qualified STMContainers.WordArray as WordArray
-import qualified STMContainers.SizedArray as SizedArray
-import qualified STMContainers.HAMT.Level as Level
-import qualified Focus
-
-
-type Nodes e = TVar (WordArray.WordArray (Node e))
-
-data Node e = 
-  Nodes {-# UNPACK #-} !(Nodes e) |
-  Leaf {-# UNPACK #-} !Hash !e |
-  Leaves {-# UNPACK #-} !Hash {-# UNPACK #-} !(SizedArray.SizedArray e)
-
-type Hash = Int
-
-class (Eq (ElementKey e)) => Element e where
-  type ElementKey e
-  elementKey :: e -> ElementKey e
-
-{-# INLINE new #-}
-new :: STM (Nodes e)
-new = newTVar WordArray.empty
-
-{-# INLINE newIO #-}
-newIO :: IO (Nodes e)
-newIO = newTVarIO WordArray.empty
-
-insert :: (Element e) => e -> Hash -> ElementKey e -> Level.Level -> Nodes e -> STM ()
-insert e h k l ns = do
-  a <- readTVar ns
-  let write n = writeTVar ns $ WordArray.set i n a
-  case WordArray.lookup i a of
-    Nothing -> write (Leaf h e)
-    Just n -> case n of
-      Nodes ns' -> insert e h k (Level.succ l) ns'
-      Leaf h' e' ->
-        if h' == h
-          then if elementKey e' == k
-            then write (Leaf h e)
-            else write (Leaves h (SizedArray.pair e e'))
-          else do
-            nodes <- pair h (Leaf h e) h' (Leaf h' e') (Level.succ l)
-            write (Nodes nodes)
-      Leaves h' la ->
-        if h' == h
-          then case SizedArray.find ((== k) . elementKey) la of
-            Just (lai, _) ->
-              write (Leaves h' (SizedArray.insert lai e la))
-            Nothing ->
-              write (Leaves h' (SizedArray.append e la))
-          else
-            write . Nodes =<< pair h (Leaf h e) h' (Leaves h' la) (Level.succ l)
-  where
-    i = Level.hashIndex l h
-
-pair :: Hash -> Node e -> Hash -> Node e -> Level.Level -> STM (Nodes e)
-pair h1 n1 h2 n2 l =
-  if i1 == i2
-    then newTVar . WordArray.singleton i1 . Nodes =<< pair h1 n1 h2 n2 (Level.succ l)
-    else newTVar $ WordArray.pair i1 n1 i2 n2
-  where
-    hashIndex = Level.hashIndex l
-    i1 = hashIndex h1
-    i2 = hashIndex h2
-
-focus :: (Element e) => Focus.StrategyM STM e r -> Hash -> ElementKey e -> Level.Level -> Nodes e -> STM r
-focus s h k l ns = do
-  a <- readTVar ns
-  (r, a'm) <- WordArray.focusM s' ai a
-  maybe (return ()) (writeTVar ns) a'm
-  return r
-  where
-    ai = Level.hashIndex l h
-    s' = \case
-      Nothing -> traversePair (return . fmap (Leaf h)) =<< s Nothing
-      Just n -> case n of
-        Nodes ns' -> do
-          r <- focus s h k (Level.succ l) ns'
-          null ns' >>= \case
-            True -> return (r, Focus.Remove)
-            False -> return (r, Focus.Keep)
-        Leaf h' e' ->
-          case h' == h of
-            True -> 
-              case elementKey e' == k of
-                True  -> 
-                  traversePair (return . fmap (Leaf h)) =<< s (Just e')
-                False -> 
-                  traversePair processDecision =<< s Nothing
-                  where
-                    processDecision = \case
-                      Focus.Replace e -> 
-                        return (Focus.Replace (Leaves h (SizedArray.pair e e')))
-                      _ -> 
-                        return Focus.Keep
-            False -> 
-              traversePair processDecision =<< s Nothing
-              where
-                processDecision = \case
-                  Focus.Replace e -> do
-                    ns' <- pair h (Leaf h e) h' (Leaf h' e') (Level.succ l)
-                    return (Focus.Replace (Nodes ns'))
-                  _ -> return Focus.Keep
-        Leaves h' a' ->
-          case h' == h of
-            True ->
-              case SizedArray.find ((== k) . elementKey) a' of
-                Just (i', e') -> 
-                  s (Just e') >>= traversePair processDecision
-                  where
-                    processDecision = \case
-                      Focus.Keep -> 
-                        return Focus.Keep
-                      Focus.Remove -> 
-                        case SizedArray.delete i' a' of
-                          a'' -> case SizedArray.null a'' of
-                            False -> return (Focus.Replace (Leaves h' a''))
-                            True -> return Focus.Remove
-                      Focus.Replace e ->
-                        return (Focus.Replace (Leaves h' (SizedArray.insert i' e a')))
-                Nothing -> 
-                  s Nothing >>= traversePair processDecision
-                  where
-                    processDecision = \case
-                      Focus.Replace e ->
-                        return (Focus.Replace (Leaves h' (SizedArray.append e a')))
-                      _ ->
-                        return Focus.Keep
-            False ->
-              s Nothing >>= traversePair processDecision
-              where
-                processDecision = \case
-                  Focus.Replace e -> do
-                    ns' <- pair h (Leaf h e) h' (Leaves h' a') (Level.succ l)
-                    return (Focus.Replace (Nodes ns'))
-                  _ ->
-                    return Focus.Keep
-
-null :: Nodes e -> STM Bool
-null = fmap WordArray.null . readTVar
-
-foldM :: (a -> e -> STM a) -> a -> Level.Level -> Nodes e -> STM a
-foldM step acc level = 
-  readTVar >=> foldlM step' acc
-  where
-    step' acc' = \case
-      Nodes ns -> foldM step acc' (Level.succ level) ns
-      Leaf _ e -> step acc' e
-      Leaves _ a -> SizedArray.foldM step acc' a
diff --git a/library/STMContainers/Map.hs b/library/STMContainers/Map.hs
deleted file mode 100644
--- a/library/STMContainers/Map.hs
+++ /dev/null
@@ -1,98 +0,0 @@
-module STMContainers.Map
-(
-  Map,
-  Key,
-  new,
-  newIO,
-  insert,
-  delete,
-  lookup,
-  focus,
-  foldM,
-  null,
-)
-where
-
-import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)
-import qualified STMContainers.HAMT as HAMT
-import qualified STMContainers.HAMT.Nodes as HAMTNodes
-import qualified Focus
-
-
--- |
--- A hash table, based on an STM-specialized hash array mapped trie.
-newtype Map k v = Map (HAMT.HAMT (Association k v))
-
--- |
--- A standard constraint for keys.
-type Key a = (Eq a, Hashable a)
-
--- |
--- A key-value association.
-type Association k v = (k, v)
-
-instance (Eq k) => HAMTNodes.Element (Association k v) where
-  type ElementKey (Association k v) = k
-  elementKey (k, v) = k
-
-{-# INLINE associationValue #-}
-associationValue :: Association k v -> v
-associationValue (_, v) = v
-
--- |
--- Look up an item.
-{-# INLINE lookup #-}
-lookup :: (Key k) => k -> Map k v -> STM (Maybe v)
-lookup k = focus Focus.lookupM k
-
--- |
--- Insert a value at a key.
-{-# INLINE insert #-}
-insert :: (Key k) => v -> k -> Map k v -> STM ()
-insert !v !k (Map h) = HAMT.insert (k, v) h
-
--- |
--- Delete an item by a key.
-{-# INLINE delete #-}
-delete :: (Key k) => k -> Map k v -> STM ()
-delete k (Map h) = HAMT.focus Focus.deleteM k h
-
--- |
--- Focus on an item by a key with a strategy.
--- 
--- This function allows to perform composite operations in a single access
--- to a map item.
--- E.g., you can look up an item and delete it at the same time,
--- or update it and return the new value.
-{-# INLINE focus #-}
-focus :: (Key k) => Focus.StrategyM STM v r -> k -> Map k v -> STM r
-focus f k (Map h) = HAMT.focus f' k h
-  where
-    f' = (fmap . fmap . fmap) (\v -> k `seq` v `seq` (k, v)) . f . fmap associationValue
-
--- |
--- Fold all the items of a map.
-{-# INLINE foldM #-}
-foldM :: (a -> (k, v) -> STM a) -> a -> Map k v -> STM a
-foldM s a (Map h) = HAMT.foldM s a h
-
--- |
--- Construct a new map.
-{-# INLINE new #-}
-new :: STM (Map k v)
-new = Map <$> HAMT.new
-
--- |
--- Construct a new map in IO.
--- 
--- This is useful for creating it on a top-level using 'unsafePerformIO', 
--- because using 'atomically' inside 'unsafePerformIO' isn't possible.
-{-# INLINE newIO #-}
-newIO :: IO (Map k v)
-newIO = Map <$> HAMT.newIO
-
--- |
--- Check, whether the map is empty.
-{-# INLINE null #-}
-null :: Map k v -> STM Bool
-null (Map h) = HAMT.null h
diff --git a/library/STMContainers/Multimap.hs b/library/STMContainers/Multimap.hs
deleted file mode 100644
--- a/library/STMContainers/Multimap.hs
+++ /dev/null
@@ -1,141 +0,0 @@
-module STMContainers.Multimap
-(
-  Multimap,
-  Association,
-  new,
-  newIO,
-  insert,
-  delete,
-  lookup,
-  focus,
-  foldM,
-  null,
-)
-where
-
-import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)
-import qualified Focus
-import qualified STMContainers.Map as Map
-import qualified STMContainers.Set as Set
-
-
--- |
--- A multimap, based on an STM-specialized hash array mapped trie.
--- 
--- Basically it's just a wrapper API around @'Map.Map' k ('Set.Set' v)@.
-newtype Multimap k v = Multimap (Map.Map k (Set.Set v))
-
--- |
--- A standard constraint for items.
-type Association k v = (Eq k, Hashable k, Eq v, Hashable v)
-
--- |
--- Look up an item by a value and a key.
-{-# INLINE lookup #-}
-lookup :: (Association k v) => v -> k -> Multimap k v -> STM Bool
-lookup v k (Multimap m) = 
-  maybe (return False) (Set.lookup v) =<< Map.lookup k m
-
--- |
--- Insert an item.
-{-# INLINABLE insert #-}
-insert :: (Association k v) => v -> k -> Multimap k v -> STM ()
-insert v k (Multimap m) =
-  Map.focus ms k m
-  where
-    ms = 
-      \case 
-        Just s -> 
-          do
-            Set.insert v s
-            return ((), Focus.Keep)
-        Nothing ->
-          do
-            s <- Set.new
-            Set.insert v s
-            return ((), Focus.Replace s)
-
--- |
--- Delete an item by a value and a key.
-{-# INLINABLE delete #-}
-delete :: (Association k v) => v -> k -> Multimap k v -> STM ()
-delete v k (Multimap m) =
-  Map.focus ms k m
-  where
-    ms = 
-      \case 
-        Just s -> 
-          do
-            Set.delete v s
-            Set.null s >>= returnDecision . bool Focus.Keep Focus.Remove
-        Nothing ->
-          returnDecision Focus.Keep
-      where
-        returnDecision c = return ((), c)
-
--- |
--- Focus on an item with a strategy by a value and a key.
--- 
--- This function allows to perform simultaneous lookup and modification.
--- 
--- The strategy is over a unit since we already know,
--- which value we're focusing on and it doesn't make sense to replace it,
--- however we still can decide wether to keep or remove it.
-{-# INLINE focus #-}
-focus :: (Association k v) => Focus.StrategyM STM () r -> v -> k -> Multimap k v -> STM r
-focus = 
-  \s v k (Multimap m) -> Map.focus (liftSetItemStrategy v s) k m
-  where
-    liftSetItemStrategy :: 
-      (Set.Element e) => e -> Focus.StrategyM STM () r -> Focus.StrategyM STM (Set.Set e) r
-    liftSetItemStrategy e s =
-      \case
-        Nothing ->
-          traversePair liftDecision =<< s Nothing
-          where
-            liftDecision =
-              \case
-                Focus.Replace b ->
-                  do
-                    s <- Set.new
-                    Set.insert e s
-                    return (Focus.Replace s)
-                _ ->
-                  return Focus.Keep
-        Just set -> 
-          do
-            r <- Set.focus s e set
-            (r,) . bool Focus.Keep Focus.Remove <$> Set.null set
-
--- |
--- Fold all the items.
-{-# INLINE foldM #-}
-foldM :: (a -> (k, v) -> STM a) -> a -> Multimap k v -> STM a
-foldM f a (Multimap m) = 
-  Map.foldM f' a m
-  where
-    f' a' (k, set) = 
-      Set.foldM f'' a' set
-      where
-        f'' a'' v = f a'' (k, v)
-
--- |
--- Construct a new multimap.
-{-# INLINE new #-}
-new :: STM (Multimap k v)
-new = Multimap <$> Map.new
-
--- |
--- Construct a new multimap in IO.
--- 
--- This is useful for creating it on a top-level using 'unsafePerformIO', 
--- because using 'atomically' inside 'unsafePerformIO' isn't possible.
-{-# INLINE newIO #-}
-newIO :: IO (Multimap k v)
-newIO = Multimap <$> Map.newIO
-
--- |
--- Check on being empty.
-{-# INLINE null #-}
-null :: Multimap k v -> STM Bool
-null (Multimap m) = Map.null m
diff --git a/library/STMContainers/Prelude.hs b/library/STMContainers/Prelude.hs
deleted file mode 100644
--- a/library/STMContainers/Prelude.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-module STMContainers.Prelude
-( 
-  module Exports,
-  bug,
-  bottom,
-  traversePair,
-)
-where
-
--- base
--------------------------
-import BasePrelude as Exports
-
--- placeholders
--------------------------
-import Development.Placeholders as Exports
-
--- hashable
--------------------------
-import Data.Hashable as Exports (Hashable(..))
-
--- custom
--------------------------
-import qualified Debug.Trace.LocationTH
-
-bug = [e| $(Debug.Trace.LocationTH.failure) . (msg <>) |]
-  where
-    msg = "A \"stm-containers\" package bug: " :: String
-
-bottom = [e| $bug "Bottom evaluated" |]
-
--- | A replacement for the missing 'Traverse' instance of pair in base < 4.7.
-traversePair :: Functor f => (a -> f b) -> (c, a) -> f (c, b)
-traversePair f (x, y) = (,) x <$> f y
diff --git a/library/STMContainers/Set.hs b/library/STMContainers/Set.hs
deleted file mode 100644
--- a/library/STMContainers/Set.hs
+++ /dev/null
@@ -1,98 +0,0 @@
-module STMContainers.Set
-(
-  Set,
-  Element,
-  new,
-  newIO,
-  insert,
-  delete,
-  lookup,
-  focus,
-  foldM,
-  null,
-)
-where
-
-import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)
-import qualified STMContainers.HAMT as HAMT
-import qualified STMContainers.HAMT.Nodes as HAMTNodes
-import qualified Focus
-
-
--- |
--- A hash set, based on an STM-specialized hash array mapped trie.
-newtype Set e = Set {hamt :: HAMT.HAMT (HAMTElement e)}
-
--- |
--- A standard constraint for elements.
-type Element a = (Eq a, Hashable a)
-
-newtype HAMTElement e = HAMTElement e
-
-instance (Eq e) => HAMTNodes.Element (HAMTElement e) where
-  type ElementKey (HAMTElement e) = e
-  elementKey (HAMTElement e) = e
-
-{-# INLINE elementValue #-}
-elementValue :: HAMTElement e -> e
-elementValue (HAMTElement e) = e
-
--- |
--- Insert a new element.
-{-# INLINE insert #-}
-insert :: (Element e) => e -> Set e -> STM ()
-insert e = HAMT.insert (HAMTElement e) . hamt
-
--- |
--- Delete an element.
-{-# INLINE delete #-}
-delete :: (Element e) => e -> Set e -> STM ()
-delete e = HAMT.focus Focus.deleteM e . hamt
-
--- |
--- Lookup an element.
-{-# INLINE lookup #-}
-lookup :: (Element e) => e -> Set e -> STM Bool
-lookup e = fmap (maybe False (const True)) . HAMT.focus Focus.lookupM e . hamt
-
--- |
--- Focus on an element with a strategy.
--- 
--- This function allows to perform simultaneous lookup and modification.
--- 
--- The strategy is over a unit since we already know, 
--- which element we're focusing on and it doesn't make sense to replace it,
--- however we still can decide wether to keep or remove it.
-{-# INLINE focus #-}
-focus :: (Element e) => Focus.StrategyM STM () r -> e -> Set e -> STM r
-focus s e = HAMT.focus elementStrategy e . hamt
-  where
-    elementStrategy = 
-      (fmap . fmap . fmap) (const (HAMTElement e)) . s . fmap (const ())
-
--- |
--- Fold all the elements.
-{-# INLINE foldM #-}
-foldM :: (a -> e -> STM a) -> a -> Set e -> STM a
-foldM f a = HAMT.foldM (\a -> f a . elementValue) a . hamt
-
--- |
--- Construct a new set.
-{-# INLINE new #-}
-new :: STM (Set e)
-new = Set <$> HAMT.new
-
--- |
--- Construct a new set in IO.
--- 
--- This is useful for creating it on a top-level using 'unsafePerformIO', 
--- because using 'atomically' inside 'unsafePerformIO' isn't possible.
-{-# INLINE newIO #-}
-newIO :: IO (Set e)
-newIO = Set <$> HAMT.newIO
-
--- |
--- Check, whether the set is empty.
-{-# INLINE null #-}
-null :: Set e -> STM Bool
-null = HAMT.null . hamt
diff --git a/library/STMContainers/SizedArray.hs b/library/STMContainers/SizedArray.hs
deleted file mode 100644
--- a/library/STMContainers/SizedArray.hs
+++ /dev/null
@@ -1,87 +0,0 @@
-module STMContainers.SizedArray where
-
-import STMContainers.Prelude hiding (lookup, toList, foldM)
-import Data.Primitive.Array
-import qualified STMContainers.Prelude as Prelude
-import qualified Focus
-
--- |
--- An array, 
--- which sacrifices the performance for space-efficiency and thread-safety.
-data SizedArray a =
-  SizedArray {-# UNPACK #-} !Int {-# UNPACK #-} !(Array a)
-
-instance Foldable SizedArray where
-  {-# INLINE foldr #-}
-  foldr step r (SizedArray size array) =
-    foldr step r $ map (indexArray array) [0 .. pred size]
-
--- |
--- An index of an element.
-type Index = Int
-
-{-# INLINE pair #-}
-pair :: a -> a -> SizedArray a
-pair e e' =
-  runST $ do
-    a <- newArray 2 e
-    writeArray a 1 e'
-    SizedArray 2 <$> unsafeFreezeArray a
-
--- |
--- Get the amount of elements.
-{-# INLINE size #-}
-size :: SizedArray a -> Int
-size (SizedArray b _) = b
-
--- |
--- Get the amount of elements.
-{-# INLINE null #-}
-null :: SizedArray a -> Bool
-null = (== 0) . size
-
-{-# INLINE find #-}
-find :: (a -> Bool) -> SizedArray a -> Maybe (Index, a)
-find p (SizedArray s a) = loop 0
-  where
-    loop i = if i < s
-      then let e = indexArray a i in if p e
-        then Just (i, e)
-        else loop (succ i)
-      else Nothing
-
--- |
--- Unsafe. Doesn't check the index overflow.
-{-# INLINE insert #-}
-insert :: Index -> a -> SizedArray a -> SizedArray a
-insert i e (SizedArray s a) = 
-  runST $ do
-    m' <- newArray s undefined
-    forM_ [0 .. pred s] $ \i' -> indexArrayM a i' >>= writeArray m' i'
-    writeArray m' i e
-    SizedArray s <$> unsafeFreezeArray m'
-
-{-# INLINE delete #-}
-delete :: Index -> SizedArray a -> SizedArray a
-delete i (SizedArray s a) = 
-  runST $ do
-    m' <- newArray (pred s) undefined
-    forM_ [0 .. pred i] $ \i' -> indexArrayM a i' >>= writeArray m' i'
-    forM_ [succ i .. pred s] $ \i' -> indexArrayM a i' >>= writeArray m' (pred i')
-    SizedArray (pred s) <$> unsafeFreezeArray m'
-
-{-# INLINE append #-}
-append :: a -> SizedArray a -> SizedArray a
-append e (SizedArray s a) =
-  runST $ do
-    m' <- newArray (succ s) undefined
-    forM_ [0 .. pred s] $ \i -> indexArrayM a i >>= writeArray m' i
-    writeArray m' s e
-    SizedArray (succ s) <$> unsafeFreezeArray m'
-
-{-# INLINE foldM #-}
-foldM :: (Monad m) => (a -> b -> m a) -> a -> SizedArray b -> m a
-foldM step acc (SizedArray size array) =
-  Prelude.foldM step' acc [0 .. pred size]
-  where
-    step' acc' i = indexArrayM array i >>= step acc'
diff --git a/library/STMContainers/WordArray.hs b/library/STMContainers/WordArray.hs
deleted file mode 100644
--- a/library/STMContainers/WordArray.hs
+++ /dev/null
@@ -1,198 +0,0 @@
-module STMContainers.WordArray where
-
-import STMContainers.Prelude hiding (lookup, toList, traverse_)
-import Data.Primitive.Array
-import qualified STMContainers.Prelude as Prelude
-import qualified STMContainers.WordArray.Indices as Indices
-import qualified Focus
-
-
--- |
--- An immutable space-efficient sparse array, 
--- which can store only as many elements as there are bits in the machine word.
-data WordArray e =
-  WordArray {-# UNPACK #-} !Indices {-# UNPACK #-} !(Array e)
-
-instance Foldable WordArray where
-  {-# INLINE foldr #-}
-  foldr step r (WordArray indices array) =
-    foldr (step . indexArray array) r $ Indices.positions indices
-
--- | 
--- A bitmap of set elements.
-type Indices = Indices.Indices
-
--- |
--- An index of an element.
-type Index = Int
-
-{-# INLINE indices #-}
-indices :: WordArray e -> Indices
-indices (WordArray b _) = b
-
-{-# INLINE maxSize #-}
-maxSize :: Int
-maxSize = Indices.maxSize
-
-{-# INLINE empty #-}
-empty :: WordArray e
-empty = WordArray 0 a
-  where
-    a = runST $ newArray 0 undefined >>= unsafeFreezeArray
-
--- |
--- An array with a single element at the specified index.
-{-# INLINE singleton #-}
-singleton :: Index -> e -> WordArray e
-singleton i e = 
-  let b = Indices.insert i 0
-      a = runST $ newArray 1 e >>= unsafeFreezeArray
-      in WordArray b a
-
-{-# INLINE pair #-}
-pair :: Index -> e -> Index -> e -> WordArray e
-pair i e i' e' =
-  WordArray is a
-  where 
-    is = Indices.fromList [i, i']
-    a = 
-      runST $ if 
-        | i < i' -> do
-          a <- newArray 2 e
-          writeArray a 1 e'
-          unsafeFreezeArray a
-        | i > i' -> do
-          a <- newArray 2 e
-          writeArray a 0 e'
-          unsafeFreezeArray a
-        | i == i' -> do
-          a <- newArray 1 e'
-          unsafeFreezeArray a
-
--- |
--- Unsafe.
--- Assumes that the list is sorted and contains no duplicate indexes.
-{-# INLINE fromList #-}
-fromList :: [(Index, e)] -> WordArray e
-fromList l = 
-  runST $ do
-    indices <- newSTRef 0
-    array <- newArray (length l) undefined
-    forM_ (zip l [0..]) $ \((i, e), ai) -> do
-      modifySTRef indices $ Indices.insert i
-      writeArray array ai e
-    WordArray <$> readSTRef indices <*> unsafeFreezeArray array
-  
-{-# INLINE toList #-}
-toList :: WordArray e -> [(Index, e)]
-toList (WordArray is a) = do
-  i <- Indices.toList is
-  e <- indexArrayM a (Indices.position i is)
-  return (i, e)
-
--- |
--- Convert into a list representation.
-{-# INLINE toMaybeList #-}
-toMaybeList :: WordArray e -> [Maybe e]
-toMaybeList w = do
-  i <- [0 .. pred Indices.maxSize] 
-  return $ lookup i w
-
-{-# INLINE elements #-}
-elements :: WordArray e -> [e]
-elements (WordArray indices array) =
-  map (\i -> indexArray array (Indices.position i indices)) .
-  Indices.toList $
-  indices
-
--- |
--- Set an element value at the index.
-{-# INLINE set #-}
-set :: Index -> e -> WordArray e -> WordArray e
-set i e (WordArray b a) = 
-  let 
-    sparseIndex = Indices.position i b
-    size = Indices.size b
-    in if Indices.elem i b
-      then 
-        let a' = runST $ do
-              ma' <- newArray size undefined
-              forM_ [0 .. (size - 1)] $ \i -> indexArrayM a i >>= writeArray ma' i
-              writeArray ma' sparseIndex e
-              unsafeFreezeArray ma'
-            in WordArray b a'
-      else
-        let a' = runST $ do
-              ma' <- newArray (size + 1) undefined
-              forM_ [0 .. (sparseIndex - 1)] $ \i -> indexArrayM a i >>= writeArray ma' i
-              writeArray ma' sparseIndex e
-              forM_ [sparseIndex .. (size - 1)] $ \i -> indexArrayM a i >>= writeArray ma' (i + 1)
-              unsafeFreezeArray ma'
-            b' = Indices.insert i b
-            in WordArray b' a'
-
--- |
--- Remove an element.
-{-# INLINE unset #-}
-unset :: Index -> WordArray e -> WordArray e
-unset i (WordArray b a) =
-  if Indices.elem i b
-    then
-      let 
-        b' = Indices.invert i b
-        a' = runST $ do
-          ma' <- newArray (pred size) undefined
-          forM_ [0 .. pred sparseIndex] $ \i -> indexArrayM a i >>= writeArray ma' i
-          forM_ [succ sparseIndex .. pred size] $ \i -> indexArrayM a i >>= writeArray ma' (pred i)
-          unsafeFreezeArray ma'
-        sparseIndex = Indices.position i b
-        size = Indices.size b
-        in WordArray b' a'
-    else WordArray b a
-
--- |
--- Lookup an item at the index.
-{-# INLINE lookup #-}
-lookup :: Index -> WordArray e -> Maybe e
-lookup i (WordArray b a) =
-  if Indices.elem i b
-    then Just (indexArray a (Indices.position i b))
-    else Nothing
-
--- |
--- Lookup strictly, using 'indexArrayM'.
-{-# INLINE lookupM #-}
-lookupM :: Monad m => Index -> WordArray e -> m (Maybe e)
-lookupM i (WordArray b a) =
-  if Indices.elem i b
-    then liftM Just (indexArrayM a (Indices.position i b))
-    else return Nothing
-
--- |
--- Check, whether there is an element at the index.
-{-# INLINE isSet #-}
-isSet :: Index -> WordArray e -> Bool
-isSet i = Indices.elem i . indices
-
--- |
--- Get the amount of elements.
-{-# INLINE size #-}
-size :: WordArray e -> Int
-size = Indices.size . indices
-
-{-# INLINE null #-}
-null :: WordArray e -> Bool
-null = Indices.null . indices
-
-{-# INLINE focusM #-}
-focusM :: Monad m => Focus.StrategyM m a r -> Index -> WordArray a -> m (r, Maybe (WordArray a))
-focusM f i w = do
-  let em = lookup i w
-  (r, c) <- f em
-  let w' = case c of
-        Focus.Keep -> Nothing
-        Focus.Remove -> case em of
-          Nothing -> Nothing
-          Just _ -> Just $ unset i w
-        Focus.Replace e' -> Just $ set i e' w
-  return (r, w')
diff --git a/library/STMContainers/WordArray/Indices.hs b/library/STMContainers/WordArray/Indices.hs
deleted file mode 100644
--- a/library/STMContainers/WordArray/Indices.hs
+++ /dev/null
@@ -1,68 +0,0 @@
-module STMContainers.WordArray.Indices where
-
-import STMContainers.Prelude hiding (toList, traverse_)
-import qualified STMContainers.Prelude as Prelude
-
-
--- |
--- A compact set of indices.
-type Indices = Int
-
-type Index = Int
-
-type Position = Int
-
--- |
--- A number of indexes, preceding this one.
-{-# INLINE position #-}
-position :: Index -> Indices -> Position
-position i b = popCount (b .&. (bit i - 1))
-
-{-# INLINE singleton #-}
-singleton :: Index -> Indices
-singleton = bit
-
-{-# INLINE insert #-}
-insert :: Index -> Indices -> Indices
-insert i = (bit i .|.)
-
-{-# INLINE invert #-}
-invert :: Index -> Indices -> Indices
-invert i = (bit i `xor`)
-
-{-# INLINE elem #-}
-elem :: Index -> Indices -> Bool
-elem = flip testBit
-
-{-# INLINE size #-}
-size :: Indices -> Int
-size = popCount
-
-{-# INLINE null #-}
-null :: Indices -> Bool
-null = (== 0)
-
-{-# INLINE maxSize #-}
-maxSize :: Int
-maxSize = bitSize (undefined :: Indices)
-
-{-# INLINE fromList #-}
-fromList :: [Index] -> Indices
-fromList = Prelude.foldr (.|.) 0 . map bit
-
-{-# INLINE toList #-}
-toList :: Indices -> [Index]
-toList w = filter (testBit w) allIndices
-
-{-# INLINE positions #-}
-positions :: Indices -> [Position]
-positions = enumFromTo 0 . pred . size
-
-{-# NOINLINE allIndices #-}
-allIndices :: [Index]
-allIndices = [0 .. pred maxSize]
-
-{-# INLINE foldr #-}
-foldr :: (Index -> r -> r) -> r -> Indices -> r
-foldr s r ix = 
-  Prelude.foldr (\i r' -> if testBit ix i then s i r' else r') r allIndices
diff --git a/library/StmContainers/Bimap.hs b/library/StmContainers/Bimap.hs
new file mode 100644
--- /dev/null
+++ b/library/StmContainers/Bimap.hs
@@ -0,0 +1,167 @@
+module StmContainers.Bimap
+  ( Bimap,
+    new,
+    newIO,
+    null,
+    size,
+    focusLeft,
+    focusRight,
+    lookupLeft,
+    lookupRight,
+    insertLeft,
+    insertRight,
+    deleteLeft,
+    deleteRight,
+    reset,
+    unfoldlM,
+    listT,
+  )
+where
+
+import qualified Focus as B
+import qualified StmContainers.Map as A
+import StmContainers.Prelude hiding (delete, empty, foldM, insert, lookup, null, toList)
+
+-- |
+-- Bidirectional map.
+-- Essentially, a bijection between subsets of its two argument types.
+--
+-- For one value of the left-hand type this map contains one value
+-- of the right-hand type and vice versa.
+data Bimap leftKey rightKey
+  = Bimap !(A.Map leftKey rightKey) !(A.Map rightKey leftKey)
+
+-- |
+-- Construct a new bimap.
+{-# INLINE new #-}
+new :: STM (Bimap leftKey rightKey)
+new =
+  Bimap <$> A.new <*> A.new
+
+-- |
+-- Construct a new bimap in IO.
+--
+-- This is useful for creating it on a top-level using 'unsafePerformIO',
+-- because using 'atomically' inside 'unsafePerformIO' isn't possible.
+{-# INLINE newIO #-}
+newIO :: IO (Bimap leftKey rightKey)
+newIO =
+  Bimap <$> A.newIO <*> A.newIO
+
+-- |
+-- Check on being empty.
+{-# INLINE null #-}
+null :: Bimap leftKey rightKey -> STM Bool
+null (Bimap leftMap _) =
+  A.null leftMap
+
+-- |
+-- Get the number of elements.
+{-# INLINE size #-}
+size :: Bimap leftKey rightKey -> STM Int
+size (Bimap leftMap _) =
+  A.size leftMap
+
+-- |
+-- Focus on a right value by the left value.
+--
+-- This function allows to perform composite operations in a single access
+-- to a map item.
+-- E.g., you can look up an item and delete it at the same time,
+-- or update it and return the new value.
+{-# INLINE focusLeft #-}
+focusLeft :: (Hashable leftKey, Hashable rightKey) => B.Focus rightKey STM result -> leftKey -> Bimap leftKey rightKey -> STM result
+focusLeft rightFocus leftKey (Bimap leftMap rightMap) =
+  do
+    ((output, change), maybeRightKey) <- A.focus (B.extractingInput (B.extractingChange rightFocus)) leftKey leftMap
+    case change of
+      B.Leave ->
+        return ()
+      B.Remove ->
+        forM_ maybeRightKey $ \oldRightKey -> A.delete oldRightKey rightMap
+      B.Set newRightKey ->
+        do
+          forM_ maybeRightKey $ \rightKey -> A.delete rightKey rightMap
+          maybeReplacedLeftKey <- A.focus (B.lookup <* B.insert leftKey) newRightKey rightMap
+          forM_ maybeReplacedLeftKey $ \replacedLeftKey -> A.delete replacedLeftKey leftMap
+    return output
+
+-- |
+-- Focus on a left value by the right value.
+--
+-- This function allows to perform composite operations in a single access
+-- to a map item.
+-- E.g., you can look up an item and delete it at the same time,
+-- or update it and return the new value.
+{-# INLINE focusRight #-}
+focusRight :: (Hashable leftKey, Hashable rightKey) => B.Focus leftKey STM result -> rightKey -> Bimap leftKey rightKey -> STM result
+focusRight valueFocus2 rightKey (Bimap leftMap rightMap) =
+  focusLeft valueFocus2 rightKey (Bimap rightMap leftMap)
+
+-- |
+-- Look up a right value by the left value.
+{-# INLINE lookupLeft #-}
+lookupLeft :: (Hashable leftKey) => leftKey -> Bimap leftKey rightKey -> STM (Maybe rightKey)
+lookupLeft leftKey (Bimap leftMap _) =
+  A.lookup leftKey leftMap
+
+-- |
+-- Look up a left value by the right value.
+{-# INLINE lookupRight #-}
+lookupRight :: (Hashable rightKey) => rightKey -> Bimap leftKey rightKey -> STM (Maybe leftKey)
+lookupRight rightKey (Bimap _ rightMap) =
+  A.lookup rightKey rightMap
+
+-- |
+-- Insert the association by the left value.
+{-# INLINE insertLeft #-}
+insertLeft :: (Hashable leftKey, Hashable rightKey) => rightKey -> leftKey -> Bimap leftKey rightKey -> STM ()
+insertLeft rightKey =
+  focusLeft (B.insert rightKey)
+
+-- |
+-- Insert the association by the right value.
+{-# INLINE insertRight #-}
+insertRight :: (Hashable leftKey, Hashable rightKey) => leftKey -> rightKey -> Bimap leftKey rightKey -> STM ()
+insertRight leftKey rightKey (Bimap leftMap rightMap) =
+  insertLeft leftKey rightKey (Bimap rightMap leftMap)
+
+-- |
+-- Delete the association by the left value.
+{-# INLINE deleteLeft #-}
+deleteLeft :: (Hashable leftKey, Hashable rightKey) => leftKey -> Bimap leftKey rightKey -> STM ()
+deleteLeft leftKey (Bimap leftMap rightMap) =
+  A.focus B.lookupAndDelete leftKey leftMap
+    >>= mapM_ (\rightKey -> A.delete rightKey rightMap)
+
+-- |
+-- Delete the association by the right value.
+{-# INLINE deleteRight #-}
+deleteRight :: (Hashable leftKey, Hashable rightKey) => rightKey -> Bimap leftKey rightKey -> STM ()
+deleteRight rightKey (Bimap leftMap rightMap) =
+  deleteLeft rightKey (Bimap rightMap leftMap)
+
+-- |
+-- Delete all the associations.
+{-# INLINE reset #-}
+reset :: Bimap leftKey rightKey -> STM ()
+reset (Bimap leftMap rightMap) =
+  do
+    A.reset leftMap
+    A.reset rightMap
+
+-- |
+-- Stream associations actively.
+--
+-- Amongst other features this function provides an interface to folding.
+{-# INLINE unfoldlM #-}
+unfoldlM :: Bimap leftKey rightKey -> UnfoldlM STM (leftKey, rightKey)
+unfoldlM (Bimap leftMap _) =
+  A.unfoldlM leftMap
+
+-- |
+-- Stream the associations passively.
+{-# INLINE listT #-}
+listT :: Bimap key value -> ListT STM (key, value)
+listT (Bimap leftMap _) =
+  A.listT leftMap
diff --git a/library/StmContainers/Map.hs b/library/StmContainers/Map.hs
new file mode 100644
--- /dev/null
+++ b/library/StmContainers/Map.hs
@@ -0,0 +1,122 @@
+module StmContainers.Map
+  ( Map,
+    new,
+    newIO,
+    null,
+    size,
+    focus,
+    lookup,
+    insert,
+    delete,
+    reset,
+    unfoldlM,
+    listT,
+    listTNonAtomic,
+  )
+where
+
+import qualified DeferredFolds.UnfoldlM as C
+import qualified Focus as B
+import StmContainers.Prelude hiding (delete, empty, foldM, insert, lookup, null, toList)
+import qualified StmHamt.Hamt as A
+
+-- |
+-- Hash-table, based on STM-specialized Hash Array Mapped Trie.
+newtype Map key value
+  = Map (A.Hamt (Product2 key value))
+
+-- |
+-- Construct a new map.
+{-# INLINEABLE new #-}
+new :: STM (Map key value)
+new =
+  Map <$> A.new
+
+-- |
+-- Construct a new map in IO.
+--
+-- This is useful for creating it on a top-level using 'unsafePerformIO',
+-- because using 'atomically' inside 'unsafePerformIO' isn't possible.
+{-# INLINEABLE newIO #-}
+newIO :: IO (Map key value)
+newIO =
+  Map <$> A.newIO
+
+-- |
+-- Check, whether the map is empty.
+{-# INLINEABLE null #-}
+null :: Map key value -> STM Bool
+null (Map hamt) =
+  A.null hamt
+
+-- |
+-- Get the number of elements.
+{-# INLINEABLE size #-}
+size :: Map key value -> STM Int
+size =
+  C.foldlM' (\x _ -> return (succ x)) 0 . unfoldlM
+
+-- |
+-- Focus on a value by the key.
+--
+-- This function allows to perform composite operations in a single access
+-- to the map's row.
+-- E.g., you can look up a value and delete it at the same time,
+-- or update it and return the new value.
+{-# INLINE focus #-}
+focus :: (Hashable key) => B.Focus value STM result -> key -> Map key value -> STM result
+focus valueFocus key (Map hamt) =
+  A.focus rowFocus (\(Product2 key _) -> key) key hamt
+  where
+    rowFocus =
+      B.mappingInput (\value -> Product2 key value) (\(Product2 _ value) -> value) valueFocus
+
+-- |
+-- Look up an item.
+{-# INLINEABLE lookup #-}
+lookup :: (Hashable key) => key -> Map key value -> STM (Maybe value)
+lookup key =
+  focus B.lookup key
+
+-- |
+-- Insert a value at a key.
+{-# INLINE insert #-}
+insert :: (Hashable key) => value -> key -> Map key value -> STM ()
+insert value key (Map hamt) =
+  void (A.insert (\(Product2 key _) -> key) (Product2 key value) hamt)
+
+-- |
+-- Delete an item by a key.
+{-# INLINEABLE delete #-}
+delete :: (Hashable key) => key -> Map key value -> STM ()
+delete key =
+  focus B.delete key
+
+-- |
+-- Delete all the associations.
+{-# INLINEABLE reset #-}
+reset :: Map key value -> STM ()
+reset (Map hamt) =
+  A.reset hamt
+
+-- |
+-- Stream the associations actively.
+--
+-- Amongst other features this function provides an interface to folding.
+{-# INLINEABLE unfoldlM #-}
+unfoldlM :: Map key value -> UnfoldlM STM (key, value)
+unfoldlM (Map hamt) =
+  fmap (\(Product2 k v) -> (k, v)) (A.unfoldlM hamt)
+
+-- |
+-- Stream the associations passively.
+{-# INLINE listT #-}
+listT :: Map key value -> ListT STM (key, value)
+listT (Map hamt) = fmap (\(Product2 k v) -> (k, v)) (A.listT hamt)
+
+-- |
+-- Stream the associations passively.
+-- Data may be inconsistent/out of date.
+{-# INLINE listTNonAtomic #-}
+listTNonAtomic :: Map key value -> ListT IO (key, value)
+listTNonAtomic (Map hamt) = fmap (\(Product2 k v) -> (k, v)) (A.listTNonAtomic hamt)
diff --git a/library/StmContainers/Multimap.hs b/library/StmContainers/Multimap.hs
new file mode 100644
--- /dev/null
+++ b/library/StmContainers/Multimap.hs
@@ -0,0 +1,181 @@
+module StmContainers.Multimap
+  ( Multimap,
+    new,
+    newIO,
+    null,
+    focus,
+    lookup,
+    lookupByKey,
+    insert,
+    delete,
+    deleteByKey,
+    reset,
+    unfoldlM,
+    unfoldlMKeys,
+    unfoldlMByKey,
+    listT,
+    listTKeys,
+    listTByKey,
+  )
+where
+
+import qualified Focus as C
+import qualified StmContainers.Map as A
+import StmContainers.Prelude hiding (delete, empty, foldM, insert, lookup, null, toList)
+import qualified StmContainers.Set as B
+
+-- |
+-- A multimap, based on an STM-specialized hash array mapped trie.
+--
+-- Basically it's just a wrapper API around @'A.Map' key ('B.Set' value)@.
+newtype Multimap key value
+  = Multimap (A.Map key (B.Set value))
+
+-- |
+-- Construct a new multimap.
+{-# INLINE new #-}
+new :: STM (Multimap key value)
+new =
+  Multimap <$> A.new
+
+-- |
+-- Construct a new multimap in IO.
+--
+-- This is useful for creating it on a top-level using 'unsafePerformIO',
+-- because using 'atomically' inside 'unsafePerformIO' isn't possible.
+{-# INLINE newIO #-}
+newIO :: IO (Multimap key value)
+newIO =
+  Multimap <$> A.newIO
+
+-- |
+-- Check on being empty.
+{-# INLINE null #-}
+null :: Multimap key value -> STM Bool
+null (Multimap map) =
+  A.null map
+
+-- |
+-- Focus on an item by the value and the key.
+--
+-- This function allows to perform simultaneous lookup and modification.
+--
+-- The focus is over a unit since we already know,
+-- which value we're focusing on and it doesn't make sense to replace it,
+-- however we still can decide wether to keep or remove it.
+{-# INLINE focus #-}
+focus :: (Hashable key, Hashable value) => C.Focus () STM result -> value -> key -> Multimap key value -> STM result
+focus unitFocus@(Focus concealUnit _) value key (Multimap map) = A.focus setFocus key map
+  where
+    setFocus = C.Focus conceal reveal
+      where
+        conceal = do
+          (output, change) <- concealUnit
+          case change of
+            C.Set () ->
+              do
+                set <- B.new
+                B.insert value set
+                return (output, C.Set set)
+            _ ->
+              return (output, C.Leave)
+    reveal set = do
+      output <- B.focus unitFocus value set
+      change <- bool C.Leave C.Remove <$> B.null set
+      return (output, change)
+
+-- |
+-- Look up an item by a value and a key.
+{-# INLINE lookup #-}
+lookup :: (Hashable key, Hashable value) => value -> key -> Multimap key value -> STM Bool
+lookup value key (Multimap m) =
+  maybe (return False) (B.lookup value) =<< A.lookup key m
+
+-- |
+-- Look up all values by key.
+{-# INLINE lookupByKey #-}
+lookupByKey :: (Hashable key) => key -> Multimap key value -> STM (Maybe (B.Set value))
+lookupByKey key (Multimap m) =
+  A.lookup key m
+
+-- |
+-- Insert an item.
+{-# INLINEABLE insert #-}
+insert :: (Hashable key, Hashable value) => value -> key -> Multimap key value -> STM ()
+insert value key (Multimap map) = A.focus setFocus key map
+  where
+    setFocus = Focus conceal reveal
+      where
+        conceal = do
+          set <- B.new
+          B.insert value set
+          return ((), C.Set set)
+        reveal set = do
+          B.insert value set
+          return ((), C.Leave)
+
+-- |
+-- Delete an item by a value and a key.
+{-# INLINEABLE delete #-}
+delete :: (Hashable key, Hashable value) => value -> key -> Multimap key value -> STM ()
+delete value key (Multimap map) = A.focus setFocus key map
+  where
+    setFocus = Focus conceal reveal
+      where
+        conceal = returnChange C.Leave
+        reveal set = do
+          B.delete value set
+          B.null set >>= returnChange . bool C.Leave C.Remove
+        returnChange c = return ((), c)
+
+-- |
+-- Delete all values associated with the key.
+{-# INLINEABLE deleteByKey #-}
+deleteByKey :: (Hashable key) => key -> Multimap key value -> STM ()
+deleteByKey key (Multimap map) =
+  A.delete key map
+
+-- |
+-- Delete all the associations.
+{-# INLINE reset #-}
+reset :: Multimap key value -> STM ()
+reset (Multimap map) =
+  A.reset map
+
+-- |
+-- Stream associations actively.
+--
+-- Amongst other features this function provides an interface to folding.
+unfoldlM :: Multimap key value -> UnfoldlM STM (key, value)
+unfoldlM (Multimap m) =
+  A.unfoldlM m >>= \(key, s) -> (key,) <$> B.unfoldlM s
+
+-- |
+-- Stream keys actively.
+unfoldlMKeys :: Multimap key value -> UnfoldlM STM key
+unfoldlMKeys (Multimap m) =
+  fmap fst (A.unfoldlM m)
+
+-- |
+-- Stream values by a key actively.
+unfoldlMByKey :: (Hashable key) => key -> Multimap key value -> UnfoldlM STM value
+unfoldlMByKey key (Multimap m) =
+  lift (A.lookup key m) >>= maybe mempty B.unfoldlM
+
+-- |
+-- Stream associations passively.
+listT :: Multimap key value -> ListT STM (key, value)
+listT (Multimap m) =
+  A.listT m >>= \(key, s) -> (key,) <$> B.listT s
+
+-- |
+-- Stream keys passively.
+listTKeys :: Multimap key value -> ListT STM key
+listTKeys (Multimap m) =
+  fmap fst (A.listT m)
+
+-- |
+-- Stream values by a key passively.
+listTByKey :: (Hashable key) => key -> Multimap key value -> ListT STM value
+listTByKey key (Multimap m) =
+  lift (A.lookup key m) >>= maybe mempty B.listT
diff --git a/library/StmContainers/Prelude.hs b/library/StmContainers/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/library/StmContainers/Prelude.hs
@@ -0,0 +1,82 @@
+module StmContainers.Prelude
+  ( module Exports,
+    modifyTVar',
+    Product2 (..),
+  )
+where
+
+import Control.Applicative as Exports
+import Control.Arrow as Exports
+import Control.Category as Exports
+import Control.Concurrent as Exports
+import Control.Exception as Exports
+import Control.Monad as Exports hiding (forM, forM_, mapM, mapM_, msum, sequence, sequence_)
+import Control.Monad.Fix as Exports hiding (fix)
+import Control.Monad.IO.Class as Exports
+import Control.Monad.ST as Exports
+import Control.Monad.Trans.Class as Exports
+import Data.Bits as Exports
+import Data.Bool as Exports
+import Data.Char as Exports
+import Data.Coerce as Exports
+import Data.Complex as Exports
+import Data.Data as Exports
+import Data.Dynamic as Exports
+import Data.Either as Exports
+import Data.Fixed as Exports
+import Data.Foldable as Exports
+import Data.Function as Exports hiding (id, (.))
+import Data.Functor as Exports hiding (unzip)
+import Data.Hashable as Exports (Hashable (..))
+import Data.IORef as Exports
+import Data.Int as Exports
+import Data.Ix as Exports
+import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons)
+import Data.Maybe as Exports
+import Data.Monoid as Exports hiding (First (..), Last (..))
+import Data.Ord as Exports
+import Data.Proxy as Exports
+import Data.Ratio as Exports
+import Data.STRef as Exports
+import Data.String as Exports
+import Data.Traversable as Exports
+import Data.Tuple as Exports
+import Data.Unique as Exports
+import Data.Version as Exports
+import Data.Word as Exports
+import Debug.Trace as Exports
+import DeferredFolds.Unfoldl as Exports (Unfoldl (..))
+import DeferredFolds.UnfoldlM as Exports (UnfoldlM (..))
+import Focus as Exports (Focus (..))
+import Foreign.ForeignPtr as Exports
+import Foreign.Ptr as Exports
+import Foreign.StablePtr as Exports
+import Foreign.Storable as Exports hiding (alignment, sizeOf)
+import GHC.Conc as Exports hiding (threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)
+import GHC.Exts as Exports (groupWith, inline, lazy, sortWith)
+import GHC.Generics as Exports (Generic)
+import GHC.IO.Exception as Exports
+import ListT as Exports (ListT (..))
+import Numeric as Exports
+import System.Environment as Exports
+import System.Exit as Exports
+import System.IO as Exports
+import System.IO.Error as Exports
+import System.IO.Unsafe as Exports
+import System.Mem as Exports
+import System.Mem.StableName as Exports
+import System.Timeout as Exports
+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readP_to_Prec, readPrec_to_P, readPrec_to_S, readS_to_Prec)
+import Text.Printf as Exports (hPrintf, printf)
+import Text.Read as Exports (Read (..), readEither, readMaybe)
+import Unsafe.Coerce as Exports
+import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))
+
+-- | Strict version of 'modifyTVar'.
+{-# INLINE modifyTVar' #-}
+modifyTVar' :: TVar a -> (a -> a) -> STM ()
+modifyTVar' var f = do
+  x <- readTVar var
+  writeTVar var $! f x
+
+data Product2 a b = Product2 !a !b deriving (Eq)
diff --git a/library/StmContainers/Set.hs b/library/StmContainers/Set.hs
new file mode 100644
--- /dev/null
+++ b/library/StmContainers/Set.hs
@@ -0,0 +1,123 @@
+module StmContainers.Set
+  ( Set,
+    new,
+    newIO,
+    null,
+    size,
+    focus,
+    lookup,
+    insert,
+    delete,
+    reset,
+    unfoldlM,
+    listT,
+    listTNonAtomic,
+  )
+where
+
+import qualified Focus as B
+import StmContainers.Prelude hiding (delete, empty, foldM, insert, lookup, null, toList)
+import qualified StmHamt.SizedHamt as A
+
+-- |
+-- A hash set, based on an STM-specialized hash array mapped trie.
+newtype Set item
+  = Set (A.SizedHamt item)
+
+-- |
+-- Construct a new set.
+{-# INLINEABLE new #-}
+new :: STM (Set item)
+new =
+  Set <$> A.new
+
+-- |
+-- Construct a new set in IO.
+--
+-- This is useful for creating it on a top-level using 'unsafePerformIO',
+-- because using 'atomically' inside 'unsafePerformIO' isn't possible.
+{-# INLINEABLE newIO #-}
+newIO :: IO (Set item)
+newIO =
+  Set <$> A.newIO
+
+-- |
+-- Check, whether the set is empty.
+{-# INLINEABLE null #-}
+null :: Set item -> STM Bool
+null (Set hamt) =
+  A.null hamt
+
+-- |
+-- Get the number of elements.
+{-# INLINEABLE size #-}
+size :: Set item -> STM Int
+size (Set hamt) =
+  A.size hamt
+
+-- |
+-- Focus on an element with a strategy.
+--
+-- This function allows to perform simultaneous lookup and modification.
+--
+-- The strategy is over a unit since we already know,
+-- which element we're focusing on and it doesn't make sense to replace it,
+-- however we still can decide wether to keep or remove it.
+{-# INLINEABLE focus #-}
+focus :: (Hashable item) => B.Focus () STM result -> item -> Set item -> STM result
+focus unitFocus item (Set hamt) =
+  A.focus rowFocus id item hamt
+  where
+    rowFocus =
+      B.mappingInput (const item) (const ()) unitFocus
+
+-- |
+-- Lookup an element.
+{-# INLINEABLE lookup #-}
+lookup :: (Hashable item) => item -> Set item -> STM Bool
+lookup =
+  focus (fmap isJust B.lookup)
+
+-- |
+-- Insert a new element.
+{-# INLINEABLE insert #-}
+insert :: (Hashable item) => item -> Set item -> STM ()
+insert item (Set hamt) =
+  A.insert id item hamt
+
+-- |
+-- Delete an element.
+{-# INLINEABLE delete #-}
+delete :: (Hashable item) => item -> Set item -> STM ()
+delete item (Set hamt) =
+  A.focus B.delete id item hamt
+
+-- |
+-- Delete all the elements.
+{-# INLINEABLE reset #-}
+reset :: Set item -> STM ()
+reset (Set hamt) =
+  A.reset hamt
+
+-- |
+-- Stream the elements actively.
+--
+-- Amongst other features this function provides an interface to folding.
+{-# INLINEABLE unfoldlM #-}
+unfoldlM :: Set item -> UnfoldlM STM item
+unfoldlM (Set hamt) =
+  A.unfoldlM hamt
+
+-- |
+-- Stream the elements passively.
+{-# INLINE listT #-}
+listT :: Set item -> ListT STM item
+listT (Set hamt) =
+  A.listT hamt
+
+-- |
+-- Stream the elements passively.
+-- Data may be inconsistent/out of date.
+{-# INLINE listTNonAtomic #-}
+listTNonAtomic :: Set item -> ListT IO item
+listTNonAtomic (Set hamt) = A.listTNonAtomic hamt
diff --git a/stm-containers.cabal b/stm-containers.cabal
--- a/stm-containers.cabal
+++ b/stm-containers.cabal
@@ -1,232 +1,150 @@
-name:
-  stm-containers
-version:
-  0.1.4
-synopsis:
-  Containers for STM
+cabal-version: 3.0
+name: stm-containers
+version: 1.2.2
+synopsis: Containers for STM
 description:
-  This library is based on an STM-specialized implementation of a
+  This library is based on an STM-specialized implementation of
   Hash Array Mapped Trie.
-  It provides efficient implementations of @Map@, @Set@ 
+  It provides efficient implementations of @Map@, @Set@
   and other data structures,
-  which are slightly slower than their counterparts from \"unordered-containers\",
-  but scale very well on concurrent access patterns.
+  which starting from version @1@ perform even better than their counterparts from \"unordered-containers\",
+  but also scale well on concurrent access patterns.
   .
-  For details on performance of the library see
+  For details on performance of the library, which are a bit outdated, see
   <http://nikita-volkov.github.io/stm-containers/ this blog post>.
-category:
-  Data Structures, STM, Concurrency
-homepage:
-  https://github.com/nikita-volkov/stm-containers 
-bug-reports:
-  https://github.com/nikita-volkov/stm-containers/issues 
-author:
-  Nikita Volkov <nikita.y.volkov@mail.ru>
-maintainer:
-  Nikita Volkov <nikita.y.volkov@mail.ru>
-copyright:
-  (c) 2014, Nikita Volkov
-license:
-  MIT
-license-file:
-  LICENSE
-build-type:
-  Simple
-cabal-version:
-  >=1.10
 
+category: Data Structures, STM, Concurrency
+homepage: https://github.com/nikita-volkov/stm-containers
+bug-reports: https://github.com/nikita-volkov/stm-containers/issues
+author: Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright: (c) 2014, Nikita Volkov
+license: MIT
+license-file: LICENSE
 
 source-repository head
-  type:
-    git
-  location:
-    git://github.com/nikita-volkov/stm-containers.git
-
+  type: git
+  location: https://github.com/nikita-volkov/stm-containers
 
 library
-  hs-source-dirs:
-    library
-  other-modules:
-    STMContainers.Prelude
-    STMContainers.WordArray.Indices
-    STMContainers.WordArray
-    STMContainers.SizedArray
-    STMContainers.HAMT.Level
-    STMContainers.HAMT.Nodes
-    STMContainers.HAMT
-  exposed-modules:
-    STMContainers.Bimap
-    STMContainers.Multimap
-    STMContainers.Map
-    STMContainers.Set
-  build-depends:
-    -- data:
-    hashable < 1.3,
-    -- control:
-    focus > 0.1.0 && < 0.2,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    primitive == 0.5.*,
-    base-prelude == 0.1.*
+  hs-source-dirs: library
   default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
+    Arrows
+    BangPatterns
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveFoldable
+    DeriveFunctor
+    DeriveGeneric
+    DeriveTraversable
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    LiberalTypeSynonyms
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    OverloadedStrings
+    ParallelListComp
+    PatternGuards
+    PatternSynonyms
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UnboxedTuples
 
+  default-language: Haskell2010
+  exposed-modules:
+    StmContainers.Bimap
+    StmContainers.Map
+    StmContainers.Multimap
+    StmContainers.Set
 
-test-suite word-array-tests
-  type:
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    executables
-    library
-  main-is:
-    WordArrayTests.hs
+  other-modules: StmContainers.Prelude
   build-depends:
-    -- testing:
-    free >= 4.6 && < 4.10,
-    mtl == 2.*,
-    QuickCheck == 2.7.*,
-    HTF == 0.11.*,
-    -- data:
-    hashable < 1.3,
-    -- control:
-    focus > 0.1.0 && < 0.2,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    primitive == 0.5.*,
-    base-prelude == 0.1.*
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
-
+    base >=4.9 && <5,
+    deferred-folds >=0.9 && <0.10,
+    focus >=1.0.1.4 && <1.1,
+    hashable >=1.4 && <2,
+    list-t >=1.0.1 && <1.1,
+    stm-hamt >=1.2.2 && <1.3,
+    transformers >=0.5 && <0.7,
 
-test-suite api-tests
-  type:
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    executables
-  main-is:
-    APITests.hs
-  build-depends:
-    QuickCheck == 2.7.*,
-    HTF == 0.11.*,
-    stm-containers,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    base-prelude == 0.1.*,
-    focus > 0.1.0 && < 0.2,
-    unordered-containers == 0.2.*,
-    free >= 4.6 && < 4.10,
-    mtl == 2.*,
-    hashable < 1.3,
-    base >= 4.5 && < 4.8
+test-suite test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
   default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
+    Arrows
+    BangPatterns
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveFoldable
+    DeriveFunctor
+    DeriveGeneric
+    DeriveTraversable
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    LiberalTypeSynonyms
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    OverloadedStrings
+    ParallelListComp
+    PatternGuards
+    PatternSynonyms
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UnboxedTuples
 
+  default-language: Haskell2010
+  main-is: Main.hs
+  other-modules:
+    Suites.Bimap
+    Suites.Map
+    Suites.Map.Update
+    Suites.Set
 
-benchmark insertion-bench
-  type: 
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    executables
-  main-is:
-    InsertionBench.hs
-  ghc-options:
-    -O2 -threaded "-with-rtsopts=-N"
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
   build-depends:
-    mwc-random == 0.13.*,
-    mwc-random-monad == 0.7.*,
-    criterion == 0.8.*,
-    -- data:
-    text < 1.2,
-    focus > 0.1.0 && < 0.2,
-    hashable < 1.3,
-    hashtables == 1.1.*,
-    containers == 0.5.*,
-    unordered-containers == 0.2.*,
-    stm-containers,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    base >= 4.5 && < 4.8
-
-
-benchmark concurrent-insertion-bench
-  type: 
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    executables
-  main-is:
-    ConcurrentInsertionBench.hs
-  ghc-options:
-    -O2 -threaded "-with-rtsopts=-N"
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
-  build-depends:
-    criterion == 0.8.*,
-    mwc-random == 0.13.*,
-    mwc-random-monad == 0.7.*,
-    -- data:
-    text < 1.2,
-    focus > 0.1.0 && < 0.2,
-    unordered-containers == 0.2.*,
-    hashable < 1.3,
-    stm-containers,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    free >= 4.5 && < 4.10,
-    async == 2.0.*,
-    base >= 4.5 && < 4.8
-
-
-benchmark concurrent-transactions-bench
-  type: 
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    executables
-  main-is:
-    ConcurrentTransactionsBench.hs
-  ghc-options:
-    -O2 -threaded "-with-rtsopts=-N"
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
-  build-depends:
-    criterion == 0.8.*,
-    mwc-random == 0.13.*,
-    mwc-random-monad == 0.7.*,
-    -- data:
-    containers >= 0.5.2 && < 0.6,
-    text < 1.2,
-    focus > 0.1.0 && < 0.2,
-    unordered-containers == 0.2.*,
-    hashable < 1.3,
+    deferred-folds,
+    focus,
+    foldl >=1.4 && <2,
+    free >=4.6 && <6,
+    list-t,
+    quickcheck-instances >=0.3.29.1 && <0.4,
+    rerebase >=1 && <2,
     stm-containers,
-    -- debugging:
-    loch-th == 0.2.*,
-    placeholders == 0.1.*,
-    -- general:
-    mtl == 2.*,
-    free >= 4.5 && < 4.10,
-    async == 2.0.*,
-    base >= 4.5 && < 4.8
+    tasty >=0.12 && <2,
+    tasty-hunit >=0.10.0.3 && <0.11,
+    tasty-quickcheck >=0.10.2 && <0.12,
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,14 @@
+import qualified Suites.Bimap
+import qualified Suites.Map
+import qualified Suites.Set
+import Test.Tasty
+import Prelude
+
+main :: IO ()
+main =
+  defaultMain
+    . testGroup ""
+    $ [ testGroup "Bimap" Suites.Bimap.tests,
+        testGroup "Map" Suites.Map.tests,
+        testGroup "Set" Suites.Set.tests
+      ]
diff --git a/test/Suites/Bimap.hs b/test/Suites/Bimap.hs
new file mode 100644
--- /dev/null
+++ b/test/Suites/Bimap.hs
@@ -0,0 +1,66 @@
+module Suites.Bimap (tests) where
+
+import qualified Focus
+import qualified ListT
+import StmContainers.Bimap
+import Test.Tasty
+import Test.Tasty.HUnit
+import Prelude
+
+tests :: [TestTree]
+tests =
+  [ testCase "construction" $ do
+      m <- newIO :: IO (Bimap Int Int)
+      atomically $ insertRight 3 1 m
+      atomically $ insertRight 4 2 m
+      assertEqual "" [(3, 1), (4, 2)] =<< atomically (ListT.toList (listT m)),
+    testCase "deleteLeft" $ do
+      m <- newIO :: IO (Bimap Int Int)
+      atomically $ insertRight 3 1 m
+      atomically $ insertRight 4 2 m
+      atomically $ deleteLeft 4 m
+      assertEqual "" [(3, 1)] =<< atomically (ListT.toList (listT m)),
+    testCase "deleteRight" $ do
+      m <- newIO :: IO (Bimap Int Int)
+      atomically $ insertRight 3 1 m
+      atomically $ insertRight 4 2 m
+      atomically $ deleteRight 2 m
+      assertEqual "" [(3, 1)] =<< atomically (ListT.toList (listT m)),
+    testCase "replacing construction" $ do
+      m <- newIO :: IO (Bimap Int Int)
+      atomically $ insertRight 3 1 m
+      atomically $ insertRight 4 2 m
+      atomically $ insertRight 3 2 m
+      assertEqual "" [(3, 2)] =<< atomically (ListT.toList (listT m)),
+    testCase "insert overwrites" $ do
+      m <- newIO :: IO (Bimap Int Int)
+      atomically $ insertRight 3 1 m
+      assertEqual "" 1 =<< atomically (size m)
+      atomically $ insertRight 3 2 m
+      assertEqual "" 1 =<< atomically (size m)
+      assertEqual "" Nothing =<< atomically (lookupRight 1 m)
+      assertEqual "" (Just 3) =<< atomically (lookupRight 2 m)
+      assertEqual "" (Just 2) =<< atomically (lookupLeft 3 m)
+      assertEqual "" Nothing =<< atomically (focusRight Focus.lookup 1 m)
+      assertEqual "" (Just 3) =<< atomically (focusRight Focus.lookup 2 m)
+      atomically $ focusRight (Focus.insert 3) 4 m
+      assertEqual "" 1 =<< atomically (size m)
+      assertEqual "" Nothing =<< atomically (lookupRight 1 m)
+      assertEqual "" Nothing =<< atomically (lookupRight 2 m)
+      assertEqual "" (Just 3) =<< atomically (lookupRight 4 m),
+    testCase "insert overwrites 2" $ do
+      m <- newIO :: IO (Bimap Int Char)
+      atomically $ insertLeft 'a' 1 m
+      assertEqual "" 1 =<< atomically (size m)
+      atomically $ insertLeft 'a' 2 m
+      assertEqual "" 1 =<< atomically (size m)
+      assertEqual "" Nothing =<< atomically (lookupLeft 1 m)
+      assertEqual "" (Just 'a') =<< atomically (lookupLeft 2 m)
+      assertEqual "" Nothing =<< atomically (focusLeft Focus.lookup 1 m)
+      assertEqual "" (Just 'a') =<< atomically (focusLeft Focus.lookup 2 m)
+      atomically $ focusLeft (Focus.insert 'a') 3 m
+      assertEqual "" 1 =<< atomically (size m)
+      assertEqual "" Nothing =<< atomically (lookupLeft 1 m)
+      assertEqual "" Nothing =<< atomically (lookupLeft 2 m)
+      assertEqual "" (Just 'a') =<< atomically (lookupLeft 3 m)
+  ]
diff --git a/test/Suites/Map.hs b/test/Suites/Map.hs
new file mode 100644
--- /dev/null
+++ b/test/Suites/Map.hs
@@ -0,0 +1,155 @@
+module Suites.Map (tests) where
+
+import qualified Control.Foldl as Foldl
+import Control.Monad.Free
+import qualified Data.HashMap.Strict as HashMap
+import qualified DeferredFolds.UnfoldlM as UnfoldlM
+import qualified Focus
+import qualified StmContainers.Map as StmMap
+import qualified Suites.Map.Update as Update
+import Test.QuickCheck.Instances ()
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import Prelude hiding (choose)
+
+interpretStmMapUpdate :: (Hashable k) => Update.Update k v -> STM (StmMap.Map k v)
+interpretStmMapUpdate update = do
+  m <- StmMap.new
+  flip iterM update $ \case
+    Update.Insert k v c -> StmMap.insert v k m >> c
+    Update.Delete k c -> StmMap.delete k m >> c
+    Update.Adjust f k c -> StmMap.focus ((Focus.adjustM . fmap return) f) k m >> c
+  return m
+
+interpretHashMapUpdate :: (Hashable k) => Update.Update k v -> HashMap.HashMap k v
+interpretHashMapUpdate update =
+  flip execState HashMap.empty $ flip iterM update $ \case
+    Update.Insert k v c -> modify (HashMap.insert k v) >> c
+    Update.Delete k c -> modify (HashMap.delete k) >> c
+    Update.Adjust f k c -> modify (adjust f k) >> c
+  where
+    adjust f k m =
+      case HashMap.lookup k m of
+        Nothing -> m
+        Just a -> HashMap.insert k (f a) m
+
+stmMapToHashMap :: (Hashable k) => StmMap.Map k v -> STM (HashMap.HashMap k v)
+stmMapToHashMap = UnfoldlM.foldM (Foldl.generalize Foldl.hashMap) . StmMap.unfoldlM
+
+stmMapFromList :: (Hashable k) => [(k, v)] -> STM (StmMap.Map k v)
+stmMapFromList list = do
+  m <- StmMap.new
+  forM_ list $ \(k, v) -> StmMap.insert v k m
+  return m
+
+stmMapToList :: StmMap.Map k v -> STM [(k, v)]
+stmMapToList = UnfoldlM.foldM (Foldl.generalize Foldl.list) . StmMap.unfoldlM
+
+-- * Intentional hash collision simulation
+
+-------------------------
+
+newtype TestKey = TestKey Word8
+  deriving (Eq, Ord, Show)
+
+instance Arbitrary TestKey where
+  arbitrary = TestKey <$> choose (0, 63)
+
+instance Hashable TestKey where
+  hashWithSalt salt (TestKey w) =
+    if odd w
+      then hashWithSalt salt (pred w)
+      else hashWithSalt salt w
+
+-- * Tests
+
+-------------------------
+
+tests :: [TestTree]
+tests =
+  [ testProperty "sizeAndList"
+      $ let gen = do
+              keys <- nub <$> listOf (choose ('a', 'z'))
+              mapM (liftA2 (flip (,)) (choose (0, 99 :: Int)) . pure) keys
+            prop list =
+              length list == stmMapLength
+              where
+                stmMapLength =
+                  unsafePerformIO $ atomically $ do
+                    x <- stmMapFromList list
+                    StmMap.size x
+         in forAll gen prop,
+    testProperty "fromListToListHashMapIsomorphism" $ \(list :: [(Text, Int)]) ->
+      let hashMapList = HashMap.toList (HashMap.fromList list)
+          stmMapList = unsafePerformIO $ atomically $ stmMapFromList list >>= stmMapToList
+       in sort hashMapList === sort stmMapList,
+    testProperty "updatesProduceTheSameEffectAsInHashMap" $ \(updates :: [Update.Update TestKey ()]) ->
+      let update = sequence_ updates
+          hashMap = interpretHashMapUpdate update
+          hashMapSize = HashMap.size hashMap
+          hashMapList = sort (HashMap.toList hashMap)
+          (stmMapList, stmMapSize) = unsafePerformIO $ atomically $ do
+            stmMap <- interpretStmMapUpdate update
+            size <- StmMap.size stmMap
+            stmMapList <- stmMapToList stmMap
+            return (sort stmMapList, size)
+       in (hashMapSize, hashMapList) === (stmMapSize, stmMapList),
+    testCase "focusInsert" $ do
+      assertEqual "" (HashMap.fromList [('a', 1), ('b', 2)]) =<< do
+        atomically $ do
+          m <- StmMap.new
+          StmMap.focus (Focus.insert 1) 'a' m
+          StmMap.focus (Focus.insert 2) 'b' m
+          stmMapToHashMap m,
+    testCase "focusInsertAndDelete" $ do
+      assertEqual "" (HashMap.fromList [('b', 2)]) =<< do
+        atomically $ do
+          m <- StmMap.new
+          StmMap.focus (Focus.insert 1) 'a' m
+          StmMap.focus (Focus.insert 2) 'b' m
+          StmMap.focus (Focus.delete) 'a' m
+          stmMapToHashMap m,
+    testCase "focusInsertAndDeleteWithCollision" $ do
+      assertEqual "" (HashMap.fromList [(TestKey 32, 2)]) =<< do
+        atomically $ do
+          m <- StmMap.new
+          StmMap.focus (Focus.insert 2) (TestKey 32) m
+          StmMap.focus (Focus.delete) (TestKey 1) m
+          stmMapToHashMap m,
+    testCase "insert" $ do
+      assertEqual "" (HashMap.fromList [('a', 1), ('b', 2), ('c', 3)]) =<< do
+        atomically $ do
+          m <- StmMap.new
+          StmMap.insert 1 'a' m
+          StmMap.insert 3 'c' m
+          StmMap.insert 2 'b' m
+          stmMapToHashMap m,
+    testCase "insert2" $ do
+      assertEqual "" (HashMap.fromList [(111 :: Int, ()), (207, ())]) =<< do
+        atomically $ do
+          m <- StmMap.new
+          StmMap.insert () 111 m
+          StmMap.insert () 207 m
+          stmMapToHashMap m,
+    testCase "adjust" $ do
+      assertEqual "" (HashMap.fromList [('a', 1), ('b', 3)]) =<< do
+        atomically $ do
+          m <- stmMapFromList [('a', 1), ('b', 2)]
+          StmMap.focus (Focus.adjustM (const $ return 3)) 'b' m
+          stmMapToHashMap m,
+    testCase "focusReachesTheTarget" $ do
+      assertEqual "" (Just 2) =<< do
+        atomically $ do
+          m <- stmMapFromList [('a', 1), ('b', 2)]
+          StmMap.focus Focus.lookup 'b' m,
+    testCase "notNull" $ do
+      assertEqual "" False =<< do
+        atomically $ StmMap.null =<< stmMapFromList [('a', ())],
+    testCase "nullAfterDeletingTheLastElement" $ do
+      assertEqual "" True =<< do
+        atomically $ do
+          m <- stmMapFromList [('a', ())]
+          StmMap.delete 'a' m
+          StmMap.null m
+  ]
diff --git a/test/Suites/Map/Update.hs b/test/Suites/Map/Update.hs
new file mode 100644
--- /dev/null
+++ b/test/Suites/Map/Update.hs
@@ -0,0 +1,52 @@
+module Suites.Map.Update where
+
+import Control.Monad.Free
+import Control.Monad.Free.TH
+import Test.QuickCheck.Instances ()
+import Test.Tasty.QuickCheck
+import Prelude hiding (delete, insert)
+
+data UpdateF k v c
+  = Insert k v c
+  | Delete k c
+  | Adjust (v -> v) k c
+  deriving (Functor)
+
+instance (Show k, Show v, Show c) => Show (UpdateF k v c) where
+  showsPrec i =
+    showParen (i > 5) . \case
+      Insert k v c ->
+        showString "Insert "
+          . showsPrecInner k
+          . showChar ' '
+          . showsPrecInner v
+          . showChar ' '
+          . showsPrecInner c
+      Delete k c ->
+        showString "Delete "
+          . showsPrecInner k
+          . showChar ' '
+          . showsPrecInner c
+      Adjust _ k c ->
+        showString "Adjust "
+          . showString "<v -> v> "
+          . showsPrecInner k
+          . showChar ' '
+          . showsPrecInner c
+    where
+      showsPrecInner = showsPrec (succ 5)
+
+instance (Show k, Show v) => Show1 (UpdateF k v) where
+  liftShowsPrec = undefined
+
+makeFree ''UpdateF
+
+type Update k v = Free (UpdateF k v) ()
+
+instance (Arbitrary k, Arbitrary v) => Arbitrary (Update k v) where
+  arbitrary =
+    frequency
+      [ (1, delete <$> arbitrary),
+        (10, insert <$> arbitrary <*> arbitrary),
+        (3, adjust <$> (const <$> arbitrary) <*> arbitrary)
+      ]
diff --git a/test/Suites/Set.hs b/test/Suites/Set.hs
new file mode 100644
--- /dev/null
+++ b/test/Suites/Set.hs
@@ -0,0 +1,125 @@
+module Suites.Set (tests) where
+
+import Control.Concurrent.STM
+import qualified Control.Foldl as Foldl
+import Control.Monad (forM_)
+import Control.Monad.Free
+import Data.Hashable
+import Data.List (nub, sort, splitAt)
+import Data.Word (Word8)
+import qualified DeferredFolds.UnfoldlM as UnfoldlM
+import qualified Focus
+import qualified ListT
+import qualified StmContainers.Set as StmSet
+import System.IO.Unsafe (unsafePerformIO)
+import Test.QuickCheck.Instances ()
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import Prelude hiding (choose, null)
+
+-- helpers
+
+stmSetFromList :: (Hashable a, Eq a) => [a] -> STM (StmSet.Set a)
+stmSetFromList xs = do
+  s <- StmSet.new
+  forM_ xs $ \x -> StmSet.insert x s
+  return s
+
+stmSetToList :: StmSet.Set a -> STM [a]
+stmSetToList = UnfoldlM.foldM (Foldl.generalize Foldl.list) . StmSet.unfoldlM
+
+-- * Intentional hash collision simulation
+
+newtype TestKey = TestKey Word8
+  deriving (Eq, Ord, Show)
+
+instance Arbitrary TestKey where
+  arbitrary = TestKey <$> choose (0, 63)
+
+instance Hashable TestKey where
+  hashWithSalt salt (TestKey w) =
+    if odd w
+      then hashWithSalt salt (pred w)
+      else hashWithSalt salt w
+
+-- * Tests
+
+tests :: [TestTree]
+tests =
+  [ testProperty "sizeAndList"
+      $ let gen = nub <$> listOf (choose ('a', 'z'))
+            prop xs =
+              length xs == stmSetSize
+              where
+                stmSetSize =
+                  unsafePerformIO $ atomically $ do
+                    s <- stmSetFromList xs
+                    StmSet.size s
+         in forAll gen prop,
+    testProperty "fromListToListSetIsomorphism" $ \(xs :: [Int]) ->
+      let setList =
+            unsafePerformIO
+              $ atomically
+              $ stmSetFromList xs
+              >>= stmSetToList
+       in sort (nub xs) === sort setList,
+    testProperty "listTNonAtomicIsomorphism" $ \(xs :: [Int]) ->
+      let setList =
+            unsafePerformIO $ do
+              set <- atomically (stmSetFromList xs)
+              ListT.toList (StmSet.listTNonAtomic set)
+       in sort (nub xs) === sort setList,
+    testProperty "insertDeleteWithCollisions" $ \(ks :: [TestKey]) ->
+      let dropped = take (length ks `div` 2) ks
+          (finalSize, finalList) =
+            unsafePerformIO $ atomically $ do
+              s <- StmSet.new
+              -- insert all
+              forM_ ks $ \k -> StmSet.insert k s
+              -- delete ~the first half of them
+              forM_ dropped $ \k -> StmSet.delete k s
+              sz <- StmSet.size s
+              ls <- stmSetToList s
+              return (sz, sort ls)
+          expected =
+            let remaining = nub (filter (`notElem` dropped) ks)
+             in (length remaining, sort remaining)
+       in (finalSize, finalList) === expected,
+    testCase "insert"
+      $ assertEqual "" (sort ['a', 'b', 'c'])
+      =<< do
+        atomically $ do
+          s <- StmSet.new
+          StmSet.insert 'a' s
+          StmSet.insert 'c' s
+          StmSet.insert 'b' s
+          sort <$> stmSetToList s,
+    testCase "focusInsert"
+      $ assertEqual "" (sort ['a', 'b'])
+      =<< do
+        atomically $ do
+          s <- StmSet.new
+          StmSet.focus (Focus.insert ()) 'a' s
+          StmSet.focus (Focus.insert ()) 'b' s
+          sort <$> stmSetToList s,
+    testCase "insertAndDelete"
+      $ assertEqual "" ['b']
+      =<< do
+        atomically $ do
+          s <- StmSet.new
+          StmSet.focus (Focus.insert ()) 'a' s
+          StmSet.focus (Focus.insert ()) 'b' s
+          StmSet.focus Focus.delete 'a' s
+          sort <$> stmSetToList s,
+    testCase "nullAndNotNull" $ do
+      assertEqual "" True =<< atomically (StmSet.null =<< StmSet.new)
+      assertEqual "" False =<< atomically (StmSet.null =<< stmSetFromList ['a']),
+    testCase "nullAfterDeletingTheLastElement"
+      $ assertEqual "" True
+      =<< do
+        atomically $ do
+          s <- stmSetFromList ['a']
+          StmSet.delete 'a' s
+          StmSet.null s
+  ]
