diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2018, Metrix.AI
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/indexation.cabal b/indexation.cabal
new file mode 100644
--- /dev/null
+++ b/indexation.cabal
@@ -0,0 +1,74 @@
+name:
+  indexation
+version:
+  0.1
+category:
+  Data
+synopsis:
+  Tools for entity indexation
+description:
+  A set of tools for indexing entities
+homepage:
+  https://github.com/nikita-volkov/indexation 
+bug-reports:
+  https://github.com/nikita-volkov/indexation/issues 
+author:
+  Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer:
+  Metrix Ninjas <ninjas@metrix.ai>
+copyright:
+  (c) 2018, Metrix.AI
+license:
+  MIT
+license-file:
+  LICENSE
+build-type:
+  Simple
+cabal-version:
+  >=1.10
+
+source-repository head
+  type:
+    git
+  location:
+    git://github.com/nikita-volkov/indexation.git
+
+library
+  hs-source-dirs:
+    library
+  default-extensions:
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples
+  default-language:
+    Haskell2010
+  exposed-modules:
+    Indexation.Index
+    Indexation.IndexTable
+    Indexation.EntityTable
+    Indexation.IO
+    Indexation.Folds
+  other-modules:
+    Indexation.Instances.Cereal
+    Indexation.Potoki.Fetch
+    Indexation.Potoki.Transform
+    Indexation.Types
+    Indexation.Cereal.Get
+    Indexation.Cereal.Put
+    Indexation.Prelude
+    Indexation.Vector
+    Indexation.HashMap
+  build-depends:
+    base >=4.7 && <5,
+    bytestring >=0.10.8 && <0.11,
+    cereal >=0.5.5 && <0.6,
+    deferred-folds >=0.4.1 && <0.5,
+    focus >=0.1.5.2 && <0.2,
+    foldl >=1 && <2,
+    hashable >=1 && <2,
+    list-t >=1 && <1.1,
+    potoki-cereal >=0.2.1.1 && <0.3,
+    potoki-core >=2.2 && <2.3,
+    profunctors >=5.2 && <6,
+    stm-containers >=0.2.16 && <0.3,
+    transformers >=0.4 && <0.6,
+    unordered-containers >=0.2.9 && <0.3,
+    vector >=0.12 && <0.13
diff --git a/library/Indexation/Cereal/Get.hs b/library/Indexation/Cereal/Get.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Cereal/Get.hs
@@ -0,0 +1,49 @@
+module Indexation.Cereal.Get
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Data.Serialize.Get
+import qualified Data.HashMap.Strict as A
+import qualified Indexation.HashMap as A
+import qualified Data.Vector as B
+
+
+getIndexHashMap :: (Eq k, Hashable k) => Get k -> Get (HashMap k Int)
+getIndexHashMap getKey =
+  getSize >>= getAssociations
+  where
+    getSize = getInt64le
+    getAssociations size = foldM step A.empty (enumFromTo 0 (pred (fromIntegral size)))
+      where
+        step hashMap index = do
+          key <- getKey
+          return (A.insert key index hashMap)
+
+getIndexTableAsEntityTable :: (Eq entity, Hashable entity) => Get entity -> Get (IndexTable entity)
+getIndexTableAsEntityTable getEntity =
+  do
+    size <- getSize
+    associations <- getAssociations size
+    return (IndexTable size associations)
+  where
+    getSize = fromIntegral <$> getInt64le
+    getAssociations size = foldM step A.empty (enumFromTo 0 (pred size))
+      where
+        step hashMap index = do
+          entity <- getEntity
+          return (A.insert entity index hashMap)
+
+getVector :: Get element -> Get (Vector element)
+getVector getElement =
+  getSize >>= getElements
+  where
+    getSize = getInt64le
+    getElements size = B.replicateM (fromIntegral size) getElement
+
+getEntityTable :: Get entity -> Get (EntityTable entity)
+getEntityTable getEntity =
+  EntityTable <$> getVector getEntity
+
+getIndex :: Get (Index entity)
+getIndex = Index . fromIntegral <$> getInt64le
diff --git a/library/Indexation/Cereal/Put.hs b/library/Indexation/Cereal/Put.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Cereal/Put.hs
@@ -0,0 +1,45 @@
+module Indexation.Cereal.Put
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Data.Serialize.Put
+import qualified Data.HashMap.Strict as A
+import qualified Indexation.HashMap as A
+import qualified Data.Vector as B
+
+
+putHashMapWithSize :: Putter k -> Putter v -> Putter (HashMap k v)
+putHashMapWithSize keyPutter valuePutter hashMap =
+  size *> associations
+  where
+    size = putInt64le (fromIntegral (A.size hashMap))
+    associations = A.traverse_ association hashMap
+    association key value = keyPutter key *> valuePutter value
+
+putVector :: Putter element -> Putter (Vector element)
+putVector putElement vector =
+  putSize *> putElements
+  where
+    putSize = putInt64le (fromIntegral (B.length vector))
+    putElements = traverse_ putElement vector
+
+{-|
+It's recommended to use 'putIndexTableAsEntityTable' instead.
+The hashmap traversal implementation is inefficient and
+the representation of 'EntityTable' is more compact.
+-}
+putIndexTableDirectly :: Putter entity -> Putter (IndexTable entity)
+putIndexTableDirectly putEntity (IndexTable size hashMap) =
+  putSize *> putAssociations
+  where
+    putSize = putInt64le (fromIntegral size)
+    putAssociations = A.traverse_ putAssociation hashMap
+    putAssociation key entity = putEntity key *> putInt64le (fromIntegral entity)
+
+putEntityTable :: Putter entity -> Putter (EntityTable entity)
+putEntityTable putEntity (EntityTable vector) =
+  putVector putEntity vector
+
+putIndex :: Putter (Index entity)
+putIndex (Index int) = putInt64le (fromIntegral int)
diff --git a/library/Indexation/EntityTable.hs b/library/Indexation/EntityTable.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/EntityTable.hs
@@ -0,0 +1,31 @@
+module Indexation.EntityTable
+(
+  EntityTable,
+  indexTable,
+  lookup,
+)
+where
+
+import Indexation.Prelude hiding (lookup)
+import Indexation.Types
+import qualified Indexation.Vector as A
+import qualified Data.Vector as B
+import qualified Indexation.Cereal.Put as C
+import qualified Indexation.Cereal.Get as D
+import qualified Data.Serialize as E
+
+
+instance E.Serialize entity => E.Serialize (EntityTable entity) where
+  put = C.putEntityTable E.put
+  get = D.getEntityTable E.get
+
+indexTable :: IndexTable entity -> EntityTable entity
+indexTable (IndexTable size table) =
+  EntityTable vector
+  where
+    vector =
+      A.indexHashMapWithSize size table
+
+lookup :: Index entity -> EntityTable entity -> Maybe entity
+lookup (Index indexPrim) (EntityTable vector) =
+  vector B.!? indexPrim
diff --git a/library/Indexation/Folds.hs b/library/Indexation/Folds.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Folds.hs
@@ -0,0 +1,46 @@
+module Indexation.Folds
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Control.Foldl
+import qualified Data.HashMap.Strict as B
+import qualified Data.Serialize as C
+import qualified Data.ByteString as D
+
+
+indexTable :: (Eq entity, Hashable entity) => Fold entity (IndexTable entity)
+indexTable =
+  Fold step init extract
+  where
+    init = IndexTable 0 B.empty
+    step (IndexTable index map) key =
+      case B.lookup key map of
+        Just _ -> IndexTable index map
+        Nothing -> let
+          newMap = B.insert key index map
+          newIndex = succ index
+          in IndexTable newIndex newMap
+    extract = id
+
+serializeToFile :: Serialize entity => FilePath -> FoldM IO entity (Either IOException ())
+serializeToFile filePath =
+  lmap C.encode (writeBytesToFile filePath)
+
+writeBytesToFile :: FilePath -> FoldM IO ByteString (Either IOException ())
+writeBytesToFile filePath =
+  FoldM step init exit
+  where
+    step :: Either IOException Handle -> ByteString -> IO (Either IOException Handle)
+    step errorOrFileHandle bytes =
+      case errorOrFileHandle of
+        Right fileHandle -> try (D.hPut fileHandle bytes $> fileHandle)
+        Left exception -> return (Left exception)
+    init :: IO (Either IOException Handle)
+    init =
+      try (openFile filePath WriteMode)
+    exit :: Either IOException Handle -> IO (Either IOException ())
+    exit errorOrFileHandle =
+      case errorOrFileHandle of
+        Right fileHandle -> try (hClose fileHandle)
+        Left exception -> return (Left exception)
diff --git a/library/Indexation/HashMap.hs b/library/Indexation/HashMap.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/HashMap.hs
@@ -0,0 +1,13 @@
+module Indexation.HashMap
+where
+
+import Indexation.Prelude
+import Data.HashMap.Strict
+
+
+traverse_ :: Applicative effect => (k -> v -> effect ()) -> HashMap k v -> effect ()
+traverse_ effect =
+  foldrWithKey step init
+  where
+    init = pure ()
+    step k v acc = effect k v *> acc
diff --git a/library/Indexation/IO.hs b/library/Indexation/IO.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/IO.hs
@@ -0,0 +1,65 @@
+module Indexation.IO
+(
+  indexProduceToFiles,
+  readEntitiesAmountFromEntityTableFile,
+)
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Indexation.Instances.Cereal ()
+import qualified Potoki.Core.IO as PotokiIo
+import qualified Potoki.Core.Consume as PotokiConsume
+import qualified Potoki.Cereal.Consume as PotokiConsume
+import qualified Potoki.Cereal.Produce as PotokiProduce
+import qualified Indexation.Potoki.Transform as PotokiTransform
+import qualified Indexation.Vector as Vector
+import qualified STMContainers.Map as StmMap
+import qualified Data.Serialize as Cereal
+import qualified Data.ByteString as ByteString
+
+
+createIndexer :: IO (Indexer entity)
+createIndexer = do
+  sizeVar <- newTVarIO 0
+  map <- StmMap.newIO
+  return (Indexer sizeVar map)
+
+createIndexerVector :: Indexer entity -> IO (Vector entity)
+createIndexerVector (Indexer sizeVar map) =
+  atomically $ do
+    size <- readTVar sizeVar
+    Vector.listT size (fmap swap (StmMap.stream map))
+
+createIndexerEntityTable :: Indexer entity -> IO (EntityTable entity)
+createIndexerEntityTable = fmap EntityTable . createIndexerVector
+
+serializeProduceEntityIndicesToFile :: (Eq entity, Hashable entity) => FilePath -> Indexer entity -> Produce entity -> IO (Either IOException ())
+serializeProduceEntityIndicesToFile path indexer entityProduce =
+  PotokiIo.produceAndConsume entityProduce $
+  PotokiConsume.transform (PotokiTransform.indexConcurrently indexer) $
+  PotokiConsume.encodeToFile path
+
+serializeIndexerToFile :: Serialize entity => FilePath -> Indexer entity -> IO (Either IOException ())
+serializeIndexerToFile path indexer = do
+  entityTable <- createIndexerEntityTable indexer
+  let
+    produce = PotokiProduce.put (Cereal.put entityTable)
+    consume = PotokiConsume.writeBytesToFile path
+    in PotokiIo.produceAndConsume produce consume
+
+indexProduceToFiles :: (Eq entity, Hashable entity, Serialize entity) => FilePath {-^ Entity table -} -> FilePath {-^ Index stream -} -> Produce entity -> IO (Either IOException ())
+indexProduceToFiles entityTablePath indexStreamPath entityProduce =
+  do
+    indexer <- createIndexer
+    runExceptT $ do
+      ExceptT (serializeProduceEntityIndicesToFile indexStreamPath indexer entityProduce)
+      ExceptT (serializeIndexerToFile entityTablePath indexer)
+
+readEntitiesAmountFromEntityTableFile :: FilePath -> IO (Either IOException Int)
+readEntitiesAmountFromEntityTableFile filePath =
+  try $ do
+    bytes <- withFile filePath ReadMode (flip ByteString.hGet 4)
+    Cereal.runGet Cereal.getInt64le bytes & \ case
+      Right x -> return (fromIntegral x)
+      Left x -> error ("Unexpected binary parsing error: " <> x)
diff --git a/library/Indexation/Index.hs b/library/Indexation/Index.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Index.hs
@@ -0,0 +1,9 @@
+module Indexation.Index
+(
+  Index(..),
+)
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Indexation.Instances.Cereal
diff --git a/library/Indexation/IndexTable.hs b/library/Indexation/IndexTable.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/IndexTable.hs
@@ -0,0 +1,40 @@
+module Indexation.IndexTable
+(
+  IndexTable,
+  lookup,
+  register,
+  empty,
+)
+where
+
+import Indexation.Prelude hiding (lookup, empty)
+import Indexation.Types
+import qualified Data.HashMap.Strict as A
+import qualified Indexation.Cereal.Get as B
+import qualified Indexation.Cereal.Put as C
+import qualified Indexation.EntityTable as D
+import qualified Data.Serialize as E
+
+
+instance (E.Serialize entity, Eq entity, Hashable entity) => E.Serialize (IndexTable entity) where
+  put = C.putEntityTable E.put . D.indexTable
+  get = B.getIndexTableAsEntityTable E.get
+
+lookup :: (Eq entity, Hashable entity) => entity -> IndexTable entity -> Maybe (Index entity)
+lookup entity (IndexTable size hashMap) =
+  fmap Index (A.lookup entity hashMap)
+
+register :: (Eq entity, Hashable entity) => entity -> IndexTable entity -> (Index entity, IndexTable entity)
+register entity (IndexTable size hashMap) =
+  A.lookup entity hashMap & \ case
+    Just index -> (Index index, IndexTable size hashMap)
+    Nothing -> let
+      newSize = succ size
+      newHashMap = A.insert entity size hashMap
+      index = Index size
+      newTable = IndexTable newSize newHashMap
+      in (index, newTable)
+
+empty :: (Eq entity, Hashable entity) => IndexTable entity
+empty =
+  IndexTable 0 mempty
diff --git a/library/Indexation/Instances/Cereal.hs b/library/Indexation/Instances/Cereal.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Instances/Cereal.hs
@@ -0,0 +1,13 @@
+module Indexation.Instances.Cereal
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Data.Serialize
+import qualified Indexation.Cereal.Get as Get
+import qualified Indexation.Cereal.Put as Put
+
+
+instance Serialize (Index a) where
+  get = Get.getIndex
+  put = Put.putIndex
diff --git a/library/Indexation/Potoki/Fetch.hs b/library/Indexation/Potoki/Fetch.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Potoki/Fetch.hs
@@ -0,0 +1,36 @@
+module Indexation.Potoki.Fetch where
+
+import Indexation.Prelude
+import Indexation.Types
+import Potoki.Core.Fetch
+import qualified Indexation.IndexTable as A
+import qualified STMContainers.Map as B
+import qualified Focus as C
+
+
+index :: (Eq entity, Hashable entity) => IORef (IndexTable entity) -> Fetch entity -> Fetch (Index entity)
+index indexTableRef (Fetch entityMaybeIO) =
+  Fetch $ do
+    entityMaybe <- entityMaybeIO
+    case entityMaybe of
+      Just entity -> do
+        indexTable <- readIORef indexTableRef
+        A.register entity indexTable & \ (!index, !newIndexTable) -> do
+          writeIORef indexTableRef newIndexTable
+          return (Just index)
+      Nothing -> return Nothing
+
+indexConcurrently :: (Eq entity, Hashable entity) => Indexer entity -> Fetch entity -> Fetch (Index entity)
+indexConcurrently (Indexer sizeVar map) (Fetch entityMaybeIO) =
+  Fetch $ do
+    entityMaybe <- entityMaybeIO
+    case entityMaybe of
+      Just entity -> fmap Just $ atomically $ B.focus strategy entity map
+      Nothing -> return Nothing
+  where
+    strategy = \ case
+      Just indexInt -> return (Index indexInt, C.Keep)
+      Nothing -> do
+        size <- readTVar sizeVar
+        writeTVar sizeVar $! succ size
+        return (Index size, C.Replace size)
diff --git a/library/Indexation/Potoki/Transform.hs b/library/Indexation/Potoki/Transform.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Potoki/Transform.hs
@@ -0,0 +1,12 @@
+module Indexation.Potoki.Transform where
+
+import Indexation.Prelude hiding (runState)
+import Indexation.Types
+import Potoki.Core.Transform
+import qualified Indexation.Potoki.Fetch as A
+import qualified Indexation.IndexTable as B
+import qualified STMContainers.Map as C
+
+
+indexConcurrently :: (Eq entity, Hashable entity) => Indexer entity -> Transform entity (Index entity)
+indexConcurrently indexer = Transform (return . A.indexConcurrently indexer)
diff --git a/library/Indexation/Prelude.hs b/library/Indexation/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Prelude.hs
@@ -0,0 +1,124 @@
+module Indexation.Prelude
+(
+  module Exports,
+)
+where
+
+
+-- base
+-------------------------
+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 (mapM_, sequence_, forM_, msum, mapM, sequence, forM)
+import Control.Monad.IO.Class as Exports
+import Control.Monad.Fix as Exports hiding (fix)
+import Control.Monad.ST 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
+import Data.Int as Exports
+import Data.IORef as Exports
+import Data.Ix as Exports
+import Data.List as Exports hiding (sortOn, isSubsequenceOf, uncons, concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')
+import Data.Maybe as Exports
+import Data.Monoid as Exports hiding (Last(..), First(..))
+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 Foreign.ForeignPtr as Exports
+import Foreign.Ptr as Exports
+import Foreign.StablePtr as Exports
+import Foreign.Storable as Exports
+import GHC.Conc as Exports hiding (withMVar, threadWaitWriteSTM, threadWaitWrite, threadWaitReadSTM, threadWaitRead)
+import GHC.Exts as Exports (lazy, inline, sortWith, groupWith)
+import GHC.Generics as Exports (Generic)
+import GHC.IO.Exception as Exports
+import Numeric as Exports
+import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, id, (.))
+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.ReadP as Exports (ReadP, ReadS, readP_to_S, readS_to_P)
+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readPrec_to_P, readP_to_Prec, readPrec_to_S, readS_to_Prec)
+import Text.Printf as Exports (printf, hPrintf)
+import Text.Read as Exports (Read(..), readMaybe, readEither)
+import Unsafe.Coerce as Exports
+
+-- transformers
+-------------------------
+import Control.Monad.IO.Class as Exports
+import Control.Monad.Trans.Class as Exports
+import Control.Monad.Trans.Cont as Exports hiding (shift, callCC)
+import Control.Monad.Trans.Except as Exports (ExceptT(ExceptT), Except, except, runExcept, runExceptT, mapExcept, mapExceptT, withExcept, withExceptT)
+import Control.Monad.Trans.Maybe as Exports
+import Control.Monad.Trans.Reader as Exports (Reader, runReader, mapReader, withReader, ReaderT(ReaderT), runReaderT, mapReaderT, withReaderT)
+import Control.Monad.Trans.State.Strict as Exports (State, runState, evalState, execState, mapState, withState, StateT(StateT), runStateT, evalStateT, execStateT, mapStateT, withStateT, state)
+import Control.Monad.Trans.Writer.Strict as Exports (Writer, runWriter, execWriter, mapWriter, WriterT(..), execWriterT, mapWriterT)
+
+-- deferred-folds
+-------------------------
+import DeferredFolds.Unfold as Exports (Unfold(..))
+
+-- foldl
+-------------------------
+import Control.Foldl as Exports (Fold(..), FoldM(..))
+
+-- hashable
+-------------------------
+import Data.Hashable as Exports (Hashable(..))
+
+-- unordered-containers
+-------------------------
+import Data.HashMap.Strict as Exports (HashMap)
+
+-- vector
+-------------------------
+import Data.Vector as Exports (Vector)
+
+-- cereal
+-------------------------
+import Data.Serialize as Exports (Serialize)
+
+-- bytestring
+-------------------------
+import Data.ByteString as Exports (ByteString)
+
+-- profunctors
+-------------------------
+import Data.Profunctor as Exports hiding (WrappedArrow(..))
+
+-- list-t
+-------------------------
+import ListT as Exports (ListT(..))
+
+-- potoki-core
+-------------------------
+import Potoki.Core.Produce as Exports (Produce)
+import Potoki.Core.Consume as Exports (Consume)
+import Potoki.Core.Transform as Exports (Transform)
+import Potoki.Core.Fetch as Exports (Fetch)
diff --git a/library/Indexation/Types.hs b/library/Indexation/Types.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Types.hs
@@ -0,0 +1,14 @@
+module Indexation.Types
+where
+
+import Indexation.Prelude
+import qualified STMContainers.Map as A
+
+
+data Indexer entity = Indexer (TVar Int) (A.Map entity Int)
+
+data IndexTable entity = IndexTable {-# UNPACK #-} !Int {-# UNPACK #-} !(HashMap entity Int)
+
+newtype EntityTable entity = EntityTable (Vector entity)
+
+newtype Index entity = Index Int
diff --git a/library/Indexation/Vector.hs b/library/Indexation/Vector.hs
new file mode 100644
--- /dev/null
+++ b/library/Indexation/Vector.hs
@@ -0,0 +1,55 @@
+module Indexation.Vector
+where
+
+import Indexation.Prelude
+import Indexation.Types
+import Data.Vector
+import qualified Data.Vector.Mutable as A
+import qualified Data.HashMap.Strict as B
+import qualified ListT
+import qualified STMContainers.Map as StmMap
+
+
+{-|
+This function is not tested.
+-}
+{-# NOINLINE populate #-}
+populate :: Monad effect => Int -> effect (Int, element) -> effect (Vector element)
+populate size effect =
+  do
+    mv <- return (unsafeDupablePerformIO (A.unsafeNew size))
+    let
+      loop stepsRemaining =
+        if stepsRemaining > 0
+          then do
+            (index, element) <- effect
+            () <- return (unsafeDupablePerformIO (A.write mv index element))
+            loop (pred stepsRemaining)
+          else do
+            !v <- return (unsafeDupablePerformIO (freeze mv))
+            return v
+      in loop size
+
+{-|
+This function is partial. It doesn't check the size or indices.
+-}
+{-# INLINE indexHashMapWithSize #-}
+indexHashMapWithSize :: Int -> HashMap element Int -> Vector element
+indexHashMapWithSize size hashMap =
+  unsafePerformIO $ do
+    mv <- A.unsafeNew size
+    let
+      step () element index = unsafeDupablePerformIO (A.write mv index element)
+      !() = B.foldlWithKey' step () hashMap
+      in freeze mv
+
+{-# NOINLINE listT #-}
+listT :: Monad m => Int -> ListT m (Int, element) -> m (Vector element)
+listT size listT =
+  let
+    step mv (index, element) = return (unsafeDupablePerformIO (A.write mv index element $> mv))
+    in do
+      !mv <- return (unsafeDupablePerformIO (A.unsafeNew size))
+      ListT.fold step mv listT
+      !iv <- return (unsafeDupablePerformIO (unsafeFreeze mv))
+      return iv
