diff --git a/edges.cabal b/edges.cabal
--- a/edges.cabal
+++ b/edges.cabal
@@ -1,7 +1,7 @@
 name:
   edges
 version:
-  0.4.1
+  0.5
 category:
   Graphs
 synopsis:
@@ -41,14 +41,17 @@
   default-language:
     Haskell2010
   exposed-modules:
-    Edges.Edges
-    Edges.NodeCounts
-    Edges.Node
-    Edges.Potoki.Produce
+    Edges.Data
+    Edges.Folds
+    Edges.NodeCounting
+    Edges.Potoki.Produces
+    Edges.Potoki.Transforms
     Edges.IO
-    Edges.Fold
+    Edges.Gens
   other-modules:
-    Edges.Cereal.Instances
+    Edges.Functions
+    Edges.Instances
+    Edges.Instances.Cereal
     Edges.Cereal.Get
     Edges.Cereal.Put
     Edges.Prelude
diff --git a/library/Edges/Cereal/Instances.hs b/library/Edges/Cereal/Instances.hs
deleted file mode 100644
--- a/library/Edges/Cereal/Instances.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module Edges.Cereal.Instances
-where
-
-import Edges.Prelude
-import Edges.Types
-import Data.Serialize
-import qualified Edges.Cereal.Get as A
-import qualified Edges.Cereal.Put as B
-
-
-instance Serialize (Node a) where
-  get = A.node
-  put = B.node
-
-instance Serialize (NodeCounts a) where
-  get = A.nodeCounts
-  put = B.nodeCounts
-
-instance Serialize (Edges a b) where
-  get = A.edges
-  put = B.edges
diff --git a/library/Edges/Data.hs b/library/Edges/Data.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Data.hs
@@ -0,0 +1,16 @@
+module Edges.Data
+(
+  Edges,
+  Node(..),
+  NodeCounts,
+  Edge(..),
+  EdgeCounts,
+  Amount(..),
+  module Functions,
+)
+where
+
+import Edges.Prelude
+import Edges.Types
+import Edges.Instances
+import Edges.Functions as Functions
diff --git a/library/Edges/Edges.hs b/library/Edges/Edges.hs
deleted file mode 100644
--- a/library/Edges/Edges.hs
+++ /dev/null
@@ -1,79 +0,0 @@
-module Edges.Edges
-(
-  Edges,
-  list,
-  listBipartite,
-  primListBipartite,
-  toAssocUnfoldM,
-  toAssocList,
-  genBipartiteWithLimits,
-)
-where
-
-import Edges.Prelude
-import Edges.Types
-import Edges.Cereal.Instances ()
-import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
-import qualified Control.Foldl as Foldl
-import qualified Control.Monad.Par as Par
-import qualified DeferredFolds.UnfoldM as UnfoldM
-import qualified Test.QuickCheck.Gen as Gen
-
-deriving instance Eq (Edges a b)
-
-deriving instance Show (Edges a b)
-
-list :: [(Node a, Node b)] -> Edges a b
-list list =
-  Par.runPar $ do
-    aSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum $ \ (Node x, _) -> x
-    bSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum $ \ (_, Node x) -> x
-    aToBPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (Node aInt, Node bInt) -> (aInt, fromIntegral bInt)
-    aSize <- Par.get aSizeFuture
-    bSize <- Par.get bSizeFuture
-    aToBEdges <- primFoldableWithAmounts aSize bSize <$> Par.get aToBPrimFoldableFuture
-    return aToBEdges
-
-listBipartite :: [(Node a, Node b)] -> (Edges a b, Edges b a)
-listBipartite = coerce primListBipartite
-
-primListBipartite :: [(Int, Int)] -> (Edges a b, Edges b a)
-primListBipartite list =
-  Par.runPar $ do
-    aSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum fst
-    bSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum snd
-    aToBPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (aInt, bInt) -> (aInt, fromIntegral bInt)
-    bToAPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (aInt, bInt) -> (bInt, fromIntegral aInt)
-    aSize <- Par.get aSizeFuture
-    bSize <- Par.get bSizeFuture
-    aToBEdgesFuture <- Par.spawn_ $ primFoldableWithAmounts aSize bSize <$> Par.get aToBPrimFoldableFuture
-    bToAEdgesFuture <- Par.spawn_ $ primFoldableWithAmounts bSize aSize <$> Par.get bToAPrimFoldableFuture
-    aToBEdges <- Par.get aToBEdgesFuture
-    bToAEdges <- Par.get bToAEdgesFuture
-    return (aToBEdges, bToAEdges)
-
-primFoldableWithAmounts :: Foldable f => Int -> Int -> f (Int, Word32) -> Edges a b
-primFoldableWithAmounts aAmount bAmount foldable =
-  Edges bAmount $ runIdentity $ PrimMultiArray.create aAmount $ \ fold ->
-  Identity $ Foldl.fold fold foldable
-
-toAssocUnfoldM :: Monad m => Edges a b -> UnfoldM m (Node a, Node b)
-toAssocUnfoldM (Edges _ mpa) =
-  fmap (\ (aInt, bWord32) -> (Node aInt, Node (fromIntegral bWord32))) $
-  PrimMultiArray.toAssocsUnfoldM mpa
-
-toAssocList :: Edges a b -> [(Node a, Node b)]
-toAssocList edges =
-  UnfoldM.fold Foldl.list (toAssocUnfoldM edges)
-
-genBipartiteWithLimits :: Int -> Int -> Gen.Gen (Edges a b, Edges b a)
-genBipartiteWithLimits nodeLimit edgeLimit =
-  do
-    aMaxIndex <- Gen.choose (0, pred nodeLimit)
-    bMaxIndex <- Gen.choose (0, pred nodeLimit)
-    edgesAmount <- Gen.choose (0, edgeLimit)
-    if aMaxIndex == 0 || bMaxIndex == 0
-      then return (primListBipartite [])
-      else do
-        edges <- replicateM edgesAmount $ (,) <$> Gen.choose (0, aMaxIndex) <*> Gen.choose (0, bMaxIndex)
-        return (primListBipartite edges)
diff --git a/library/Edges/Fold.hs b/library/Edges/Fold.hs
deleted file mode 100644
--- a/library/Edges/Fold.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-module Edges.Fold
-where
-
-import Edges.Prelude
-import Edges.Types
-import Edges.Node ()
-import Edges.NodeCounts ()
-import qualified PrimitiveExtras.PrimArray as PrimArray
-import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
-
-
-edgeCounts :: Amount a -> Fold (Edge a b) (EdgeCounts a b)
-edgeCounts (Amount amountInt) =
-  dimap edgeSourceIndex EdgeCounts (PrimArray.indexCountsFold amountInt)
-  where
-    edgeSourceIndex (Edge sourceIndex _) = sourceIndex
-
-edges :: EdgeCounts a b -> Amount b -> Fold (Edge a b) (Edges a b)
-edges (EdgeCounts edgeCountsPrimArray) (Amount bAmountInt) =
-  dimap edgePair (Edges bAmountInt) (PrimMultiArray.fold edgeCountsPrimArray)
-  where
-    edgePair (Edge sourceIndex targetIndex) = (sourceIndex, targetIndex)
diff --git a/library/Edges/Folds.hs b/library/Edges/Folds.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Folds.hs
@@ -0,0 +1,21 @@
+module Edges.Folds
+where
+
+import Edges.Prelude
+import Edges.Types
+import Edges.Data
+import qualified PrimitiveExtras.PrimArray as PrimArray
+import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
+
+
+edgeCounts :: Amount a -> Fold (Edge a b) (EdgeCounts a b)
+edgeCounts (Amount amountInt) =
+  dimap edgeSourceIndex EdgeCounts (PrimArray.indexCountsFold amountInt)
+  where
+    edgeSourceIndex (Edge sourceIndex _) = sourceIndex
+
+edges :: EdgeCounts a b -> Amount b -> Fold (Edge a b) (Edges a b)
+edges (EdgeCounts edgeCountsPrimArray) (Amount bAmountInt) =
+  dimap edgePair (Edges bAmountInt) (PrimMultiArray.fold edgeCountsPrimArray)
+  where
+    edgePair (Edge sourceIndex targetIndex) = (sourceIndex, targetIndex)
diff --git a/library/Edges/Functions.hs b/library/Edges/Functions.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Functions.hs
@@ -0,0 +1,67 @@
+module Edges.Functions
+where
+
+import Edges.Prelude
+import Edges.Types
+import qualified Data.Vector.Unboxed as UnboxedVector
+import qualified DeferredFolds.UnfoldM as UnfoldM
+import qualified Control.Foldl as Foldl
+import qualified Control.Monad.Par as Par
+import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
+import qualified PrimitiveExtras.PrimArray as PrimArray
+
+
+edgesSourceAmount :: Edges source x -> Amount source
+edgesSourceAmount (Edges _ pma) = Amount (PrimMultiArray.outerLength pma)
+
+edgesTargetAmount :: Edges x target -> Amount target
+edgesTargetAmount (Edges amount _) = Amount amount
+
+edgesUnfoldM :: Monad m => Edges a b -> UnfoldM m (Node a, Node b)
+edgesUnfoldM (Edges _ mpa) =
+  fmap (\ (aInt, bWord32) -> (Node aInt, Node (fromIntegral bWord32))) $
+  PrimMultiArray.toAssocsUnfoldM mpa
+
+edgesList :: Edges a b -> [(Node a, Node b)]
+edgesList edges =
+  UnfoldM.fold Foldl.list (edgesUnfoldM edges)
+
+listEdges :: [(Node a, Node b)] -> Edges a b
+listEdges list =
+  Par.runPar $ do
+    aSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum $ \ (Node x, _) -> x
+    bSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum $ \ (_, Node x) -> x
+    aToBPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (Node aInt, Node bInt) -> (aInt, fromIntegral bInt)
+    aSize <- Par.get aSizeFuture
+    bSize <- Par.get bSizeFuture
+    aToBEdges <- primFoldableWithAmountsEdges aSize bSize <$> Par.get aToBPrimFoldableFuture
+    return aToBEdges
+
+listBipartiteEdges :: [(Node a, Node b)] -> (Edges a b, Edges b a)
+listBipartiteEdges = coerce primListBipartiteEdges
+
+primListBipartiteEdges :: [(Int, Int)] -> (Edges a b, Edges b a)
+primListBipartiteEdges list =
+  Par.runPar $ do
+    aSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum fst
+    bSizeFuture <- Par.spawnP $ succ $ fromMaybe 0 $ flip Foldl.fold list $ flip lmap Foldl.maximum snd
+    aToBPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (aInt, bInt) -> (aInt, fromIntegral bInt)
+    bToAPrimFoldableFuture <- Par.spawnP $ flip fmap list $ \ (aInt, bInt) -> (bInt, fromIntegral aInt)
+    aSize <- Par.get aSizeFuture
+    bSize <- Par.get bSizeFuture
+    aToBEdgesFuture <- Par.spawn_ $ primFoldableWithAmountsEdges aSize bSize <$> Par.get aToBPrimFoldableFuture
+    bToAEdgesFuture <- Par.spawn_ $ primFoldableWithAmountsEdges bSize aSize <$> Par.get bToAPrimFoldableFuture
+    aToBEdges <- Par.get aToBEdgesFuture
+    bToAEdges <- Par.get bToAEdgesFuture
+    return (aToBEdges, bToAEdges)
+
+primFoldableWithAmountsEdges :: Foldable f => Int -> Int -> f (Int, Word32) -> Edges a b
+primFoldableWithAmountsEdges aAmount bAmount foldable =
+  Edges bAmount $ runIdentity $ PrimMultiArray.create aAmount $ \ fold ->
+  Identity $ Foldl.fold fold foldable
+
+nodeCountsList :: NodeCounts entity -> [Word32]
+nodeCountsList (NodeCounts pa) = foldrPrimArray' (:) [] pa
+
+nodeCountsUnboxedVector :: NodeCounts entity -> UnboxedVector.Vector Word32
+nodeCountsUnboxedVector (NodeCounts pa) = PrimArray.toUnboxedVector pa
diff --git a/library/Edges/Gens.hs b/library/Edges/Gens.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Gens.hs
@@ -0,0 +1,22 @@
+module Edges.Gens
+where
+
+import Edges.Prelude hiding (choose)
+import Edges.Data
+import Test.QuickCheck.Gen
+
+
+nodeWithLimit :: Int -> Gen (Node a)
+nodeWithLimit max = Node <$> choose (0, max)
+
+bipartiteEdgesWithLimits :: Int -> Int -> Gen (Edges a b, Edges b a)
+bipartiteEdgesWithLimits nodeLimit edgeLimit =
+  do
+    aMaxIndex <- choose (0, pred nodeLimit)
+    bMaxIndex <- choose (0, pred nodeLimit)
+    edgesAmount <- choose (0, edgeLimit)
+    if aMaxIndex == 0 || bMaxIndex == 0
+      then return (primListBipartiteEdges [])
+      else do
+        edges <- replicateM edgesAmount $ (,) <$> choose (0, aMaxIndex) <*> choose (0, bMaxIndex)
+        return (primListBipartiteEdges edges)
diff --git a/library/Edges/IO.hs b/library/Edges/IO.hs
--- a/library/Edges/IO.hs
+++ b/library/Edges/IO.hs
@@ -2,17 +2,14 @@
 where
 
 import Edges.Prelude
-import Edges.Types
-import Edges.Node ()
-import Edges.NodeCounts ()
+import Edges.Data
 import Potoki.IO
-import qualified Edges.Potoki.Produce as A
-import qualified Potoki.Transform as B
+import qualified Edges.Potoki.Produces as A
 import qualified Potoki.Cereal.Consume as C
 
 
-encodeNodeCountsToFile :: FilePath -> Edges a x -> (Node a -> NodeCounts z) -> IO (Either IOException ())
-encodeNodeCountsToFile file edges nodeCounts =
+encodeNodeCountsToFile :: FilePath -> Amount a -> (Node a -> NodeCounts z) -> IO (Either IOException ())
+encodeNodeCountsToFile file amount nodeCounts =
   produceAndConsume
-    (A.nodeCounts edges nodeCounts)
+    (A.nodeCounts amount nodeCounts)
     (C.encodeToFile file)
diff --git a/library/Edges/Instances.hs b/library/Edges/Instances.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Instances.hs
@@ -0,0 +1,17 @@
+module Edges.Instances
+where
+
+import Edges.Prelude
+import Edges.Types
+import Edges.Functions
+import Edges.Instances.Cereal ()
+
+
+deriving instance Eq (Node a)
+deriving instance Eq (Edges a b)
+
+deriving instance Ord (Node a)
+
+instance Show (Node a) where show (Node int) = show int
+instance Show (NodeCounts a) where show = show . nodeCountsList
+deriving instance Show (Edges a b)
diff --git a/library/Edges/Instances/Cereal.hs b/library/Edges/Instances/Cereal.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Instances/Cereal.hs
@@ -0,0 +1,21 @@
+module Edges.Instances.Cereal
+where
+
+import Edges.Prelude
+import Edges.Types
+import Data.Serialize
+import qualified Edges.Cereal.Get as A
+import qualified Edges.Cereal.Put as B
+
+
+instance Serialize (Node a) where
+  get = A.node
+  put = B.node
+
+instance Serialize (NodeCounts a) where
+  get = A.nodeCounts
+  put = B.nodeCounts
+
+instance Serialize (Edges a b) where
+  get = A.edges
+  put = B.edges
diff --git a/library/Edges/Node.hs b/library/Edges/Node.hs
deleted file mode 100644
--- a/library/Edges/Node.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module Edges.Node
-(
-  Node(..),
-  genWithLimit,
-)
-where
-
-import Edges.Prelude
-import Edges.Types
-import Edges.Cereal.Instances ()
-import qualified Test.QuickCheck.Gen as Gen
-
-instance Show (Node a) where
-  show (Node int) = show int
-
-deriving instance Eq (Node a)
-
-deriving instance Ord (Node a)
-
-genWithLimit :: Int -> Gen.Gen (Node a)
-genWithLimit max = Node <$> Gen.choose (0, max)
diff --git a/library/Edges/NodeCounting.hs b/library/Edges/NodeCounting.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/NodeCounting.hs
@@ -0,0 +1,58 @@
+module Edges.NodeCounting
+(
+  NodeCounts,
+  Node,
+  Amount,
+  Edges,
+  node,
+  nodeTargets,
+  targets,
+)
+where
+
+import Edges.Prelude hiding (index, toList)
+import Edges.Types
+import Edges.Functions
+import Edges.Instances ()
+import qualified PrimitiveExtras.PrimArray as PrimArray
+import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
+import qualified PrimitiveExtras.TVarArray as TVarArray
+import qualified DeferredFolds.UnfoldM as UnfoldM
+import qualified Data.Vector.Unboxed as UnboxedVector
+import qualified Control.Monad.Par.IO as Par
+import qualified Control.Monad.Par as Par hiding (runParIO)
+
+
+node :: Edges source target -> Node source -> NodeCounts source
+node edges = 
+  nodeWithAmount (edgesSourceAmount edges)
+
+nodeWithAmount :: Amount entity -> Node entity -> NodeCounts entity
+nodeWithAmount (Amount size) (Node index) =
+  NodeCounts (PrimArray.oneHot size index 1)
+
+nodeTargets :: Edges source target -> Node source -> NodeCounts target
+nodeTargets (Edges targetAmount edgesPma) (Node sourceIndex) =
+  let indexUnfold = fmap fromIntegral (PrimMultiArray.toUnfoldAtM edgesPma sourceIndex)
+      indexFold = PrimArray.indexCountsFold targetAmount
+      countPa = UnfoldM.fold indexFold indexUnfold
+      in NodeCounts countPa
+
+{-|
+Count the occurrences of targets based on the occurrences of sources.
+
+Utilizes concurrency.
+-}
+targets :: Edges source target -> NodeCounts source -> NodeCounts target
+targets (Edges targetAmount edgesPma) (NodeCounts sourceCountsPa) =
+  unsafePerformIO $ Par.runParIO $ do
+    targetCountVarTable <- liftIO (TVarArray.new 0 targetAmount)
+    Par.parFor (Par.InclusiveRange 0 (pred (sizeofPrimArray sourceCountsPa))) $ \ sourceIndex ->
+      case indexPrimArray sourceCountsPa sourceIndex of
+        0 -> return ()
+        sourceCount ->
+          liftIO $
+          UnfoldM.forM_ (PrimMultiArray.toUnfoldAtM edgesPma sourceIndex) $ \ targetIndex ->
+          TVarArray.modifyAt targetCountVarTable (fromIntegral targetIndex) (+ sourceCount)
+    targetCountsPa <- liftIO (TVarArray.freezeAsPrimArray targetCountVarTable)
+    return (NodeCounts targetCountsPa)
diff --git a/library/Edges/NodeCounts.hs b/library/Edges/NodeCounts.hs
deleted file mode 100644
--- a/library/Edges/NodeCounts.hs
+++ /dev/null
@@ -1,66 +0,0 @@
-module Edges.NodeCounts
-(
-  NodeCounts,
-  node,
-  nodeTargets,
-  targets,
-  toList,
-  toUnboxedVector,
-)
-where
-
-import Edges.Prelude hiding (index, toList)
-import Edges.Types
-import Edges.Cereal.Instances ()
-import qualified PrimitiveExtras.PrimArray as PrimArray
-import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
-import qualified PrimitiveExtras.TVarArray as TVarArray
-import qualified DeferredFolds.UnfoldM as UnfoldM
-import qualified Data.Vector.Unboxed as UnboxedVector
-import qualified Control.Monad.Par.IO as Par
-import qualified Control.Monad.Par as Par hiding (runParIO)
-
-
-instance Show (NodeCounts a) where
-  show = show . toList
-
-node :: Edges entity anyEntity -> Node entity -> NodeCounts entity
-node (Edges _ edgesPma) =
-  let size = PrimMultiArray.outerLength edgesPma
-      in nodeWithSize size
-
-nodeWithSize :: Int -> Node entity -> NodeCounts entity
-nodeWithSize size (Node index) =
-  NodeCounts (PrimArray.oneHot size index 1)
-
-nodeTargets :: Edges source target -> Node source -> NodeCounts target
-nodeTargets (Edges targetAmount edgesPma) (Node sourceIndex) =
-  let indexUnfold = fmap fromIntegral (PrimMultiArray.toUnfoldAtM edgesPma sourceIndex)
-      indexFold = PrimArray.indexCountsFold targetAmount
-      countPa = UnfoldM.fold indexFold indexUnfold
-      in NodeCounts countPa
-
-{-|
-Count the occurrences of targets based on the occurrences of sources.
-
-Utilizes concurrency.
--}
-targets :: Edges source target -> NodeCounts source -> NodeCounts target
-targets (Edges targetAmount edgesPma) (NodeCounts sourceCountsPa) =
-  unsafePerformIO $ Par.runParIO $ do
-    targetCountVarTable <- liftIO (TVarArray.new 0 targetAmount)
-    Par.parFor (Par.InclusiveRange 0 (pred (sizeofPrimArray sourceCountsPa))) $ \ sourceIndex ->
-      case indexPrimArray sourceCountsPa sourceIndex of
-        0 -> return ()
-        sourceCount ->
-          liftIO $
-          UnfoldM.forM_ (PrimMultiArray.toUnfoldAtM edgesPma sourceIndex) $ \ targetIndex ->
-          TVarArray.modifyAt targetCountVarTable (fromIntegral targetIndex) (+ sourceCount)
-    targetCountsPa <- liftIO (TVarArray.freezeAsPrimArray targetCountVarTable)
-    return (NodeCounts targetCountsPa)
-
-toList :: NodeCounts entity -> [Word32]
-toList (NodeCounts pa) = foldrPrimArray' (:) [] pa
-
-toUnboxedVector :: NodeCounts entity -> UnboxedVector.Vector Word32
-toUnboxedVector (NodeCounts pa) = PrimArray.toUnboxedVector pa
diff --git a/library/Edges/Potoki/Produce.hs b/library/Edges/Potoki/Produce.hs
deleted file mode 100644
--- a/library/Edges/Potoki/Produce.hs
+++ /dev/null
@@ -1,25 +0,0 @@
-module Edges.Potoki.Produce
-where
-
-import Edges.Prelude
-import Edges.Types
-import Edges.NodeCounts ()
-import Potoki.Produce
-import qualified PrimitiveExtras.PrimMultiArray as PrimMultiArray
-import qualified Potoki.Transform as B
-import qualified Potoki.Cereal.Produce as C
-
-
-nodes :: Edges a x -> Produce (Node a)
-nodes (Edges _ pma) =
-  coerce $
-  enumInRange 0 (pred (PrimMultiArray.outerLength pma))
-
-nodeCounts :: Edges a x -> (Node a -> NodeCounts b) -> Produce (Node a, NodeCounts b)
-nodeCounts edges nodeCounts =
-  transform (B.concurrently numCapabilities (arr (\ node -> case nodeCounts node of x -> (node, x)))) $
-  nodes edges
-
-nodeCountsFromFile :: FilePath -> Produce (Either IOException (Either Text (Node a, NodeCounts b)))
-nodeCountsFromFile file =
-  C.fileDecoded file
diff --git a/library/Edges/Potoki/Produces.hs b/library/Edges/Potoki/Produces.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Potoki/Produces.hs
@@ -0,0 +1,27 @@
+module Edges.Potoki.Produces
+where
+
+import Edges.Prelude
+import Edges.Types
+import Edges.Instances
+import Potoki.Produce
+import qualified Potoki.Cereal.Produce as Produce
+import qualified Edges.Potoki.Transforms as Transforms
+
+
+{-|
+Enumerate nodes.
+-}
+sourceNodes :: Amount a -> Produce (Node a)
+sourceNodes (Amount amountInt) = coerce (enumInRange 0 (pred amountInt))
+
+{-|
+Node counts paired with the source nodes.
+-}
+nodeCounts :: Amount a -> (Node a -> NodeCounts b) -> Produce (Node a, NodeCounts b)
+nodeCounts amount nodeCounts =
+  transform (Transforms.executeNodeCountQuery nodeCounts) (sourceNodes amount)
+
+readNodeCountsFromFile :: FilePath -> Produce (Either IOException (Either Text (Node a, NodeCounts b)))
+readNodeCountsFromFile file =
+  Produce.fileDecoded file
diff --git a/library/Edges/Potoki/Transforms.hs b/library/Edges/Potoki/Transforms.hs
new file mode 100644
--- /dev/null
+++ b/library/Edges/Potoki/Transforms.hs
@@ -0,0 +1,15 @@
+module Edges.Potoki.Transforms
+where
+
+import Edges.Prelude
+import Edges.Types
+import Edges.Instances
+import Potoki.Transform
+
+
+{-|
+Node counts paired with the source nodes.
+-}
+executeNodeCountQuery :: (Node a -> NodeCounts b) -> Transform (Node a) (Node a, NodeCounts b)
+executeNodeCountQuery nodeCounts =
+  concurrently numCapabilities (arr (\ node -> case nodeCounts node of x -> (node, x)))
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -7,10 +7,11 @@
 import Test.Tasty.Runners
 import Test.Tasty.HUnit
 import Test.Tasty.QuickCheck
-import qualified Edges.Edges as A
-import qualified Edges.NodeCounts as B
-import qualified Edges.Node as C
-import qualified Data.Serialize as D
+import Edges.Data
+import qualified Edges.NodeCounting as NodeCounting
+import qualified Edges.Data as Data
+import qualified Edges.Gens as Gens
+import qualified Data.Serialize as Cereal
 
 
 main =
@@ -18,9 +19,9 @@
   testGroup "All tests" $
   [
     testGroup "Predefined bipartite" $ let
-      edgeList :: [(C.Node (Proxy 1), C.Node (Proxy 2))]
+      edgeList :: [(Node (Proxy 1), Node (Proxy 2))]
       edgeList =
-        fmap (bimap C.Node C.Node) $
+        fmap (bimap Node Node) $
         [
           (0, 0),
           (0, 1),
@@ -29,40 +30,40 @@
           (1, 1),
           (2, 0)
         ]
-      (edges1, edges2) = A.listBipartite edgeList
+      (edges1, edges2) = Data.listBipartiteEdges edgeList
       in
         [
           testCase "Constructs the forward edges correctly" $ let
-            reconstructedEdgeList = A.toAssocList edges1
+            reconstructedEdgeList = Data.edgesList edges1
             in assertEqual (show reconstructedEdgeList) edgeList reconstructedEdgeList
           ,
           testCase "Constructs the backward edges correctly" $ let
-            reconstructedEdgeList = sort $ A.toAssocList edges2
+            reconstructedEdgeList = sort $ Data.edgesList edges2
             expectedEdgeList = sort $ fmap swap edgeList
             in assertEqual (show reconstructedEdgeList) expectedEdgeList reconstructedEdgeList
           ,
           testGroup "Counting at depth" $ let
-            node = C.Node 1 :: C.Node (Proxy 1)
+            node = Node 1 :: Node (Proxy 1)
             in
               [
-                testCase "0" $ let
-                  nodeCountsList = B.node edges1 node & B.toList
+                testCase "0, unoptimized" $ let
+                  nodeCountsList = NodeCounting.node edges1 node & Data.nodeCountsList
                   in assertEqual (show nodeCountsList) [0, 1, 0] nodeCountsList
                 ,
                 testCase "1, unoptimized" $ let
-                  nodeCountsList = B.node edges1 node & B.targets edges1 & B.toList
+                  nodeCountsList = NodeCounting.node edges1 node & NodeCounting.targets edges1 & Data.nodeCountsList
                   in assertEqual (show nodeCountsList) [1, 1, 0] nodeCountsList
                 ,
                 testCase "1" $ let
-                  nodeCountsList = B.nodeTargets edges1 node & B.toList
+                  nodeCountsList = NodeCounting.nodeTargets edges1 node & Data.nodeCountsList
                   in assertEqual (show nodeCountsList) [1, 1, 0] nodeCountsList
                 ,
                 testCase "2" $ let
-                  nodeCountsList = B.nodeTargets edges1 node & B.targets edges2 & B.toList
+                  nodeCountsList = NodeCounting.nodeTargets edges1 node & NodeCounting.targets edges2 & Data.nodeCountsList
                   in assertEqual (show nodeCountsList) [2, 2, 1] nodeCountsList
                 ,
                 testCase "3" $ let
-                  nodeCountsList = B.nodeTargets edges1 node & B.targets edges2 & B.targets edges1 & B.toList
+                  nodeCountsList = NodeCounting.nodeTargets edges1 node & NodeCounting.targets edges2 & NodeCounting.targets edges1 & Data.nodeCountsList
                   in
                     {-
                     [0, 1]
@@ -74,6 +75,6 @@
               ]
         ]
     ,
-    testProperty "Encoding/decoding with Cereal" $ forAll (A.genBipartiteWithLimits 10 20) $ \ (edges1, edges2) ->
-    D.decode (D.encode edges1) === Right edges1
+    testProperty "Encoding/decoding with Cereal" $ forAll (Gens.bipartiteEdgesWithLimits 10 20) $ \ (edges1, edges2) ->
+    Cereal.decode (Cereal.encode edges1) === Right edges1
   ]
