diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Kai Zhang
+
+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/cbits/Selector.chs.c b/cbits/Selector.chs.c
new file mode 100644
--- /dev/null
+++ b/cbits/Selector.chs.c
@@ -0,0 +1,21 @@
+#include "IGraph/Internal/Selector.chs.h"
+igraph_vit_t * __c2hs_wrapped__igraph_vit_new(const igraph_t * graph,
+                                              igraph_vs_t * vs)
+{
+    return igraph_vit_new(graph, *vs);
+}
+igraph_eit_t * __c2hs_wrapped__igraph_eit_new(const igraph_t * graph,
+                                              igraph_es_t * es)
+{
+    return igraph_eit_new(graph, *es);
+}
+int __c2hs_wrapped__igraph_delete_vertices(igraph_t * graph,
+                                           const igraph_vs_t * vertices)
+{
+    return igraph_delete_vertices(graph, *vertices);
+}
+int __c2hs_wrapped__igraph_delete_edges(igraph_t * graph,
+                                        igraph_es_t * edges)
+{
+    return igraph_delete_edges(graph, *edges);
+}
diff --git a/cbits/Structure.chs.c b/cbits/Structure.chs.c
new file mode 100644
--- /dev/null
+++ b/cbits/Structure.chs.c
@@ -0,0 +1,78 @@
+#include "IGraph/Internal/Structure.chs.h"
+int __c2hs_wrapped__igraph_personalized_pagerank(const igraph_t * graph,
+                                                 igraph_pagerank_algo_t algo,
+                                                 igraph_vector_t * vector,
+                                                 igraph_real_t * value,
+                                                 const igraph_vs_t * vids,
+                                                 igraph_bool_t directed,
+                                                 igraph_real_t damping,
+                                                 igraph_vector_t * reset,
+                                                 const igraph_vector_t * weights,
+                                                 void * options)
+{
+    return igraph_personalized_pagerank(graph,
+                                        algo,
+                                        vector,
+                                        value,
+                                        *vids,
+                                        directed,
+                                        damping,
+                                        reset,
+                                        weights,
+                                        options);
+}
+int __c2hs_wrapped__igraph_pagerank(const igraph_t * graph,
+                                    igraph_pagerank_algo_t algo,
+                                    igraph_vector_t * vector,
+                                    igraph_real_t * value,
+                                    const igraph_vs_t * vids,
+                                    igraph_bool_t directed,
+                                    igraph_real_t damping,
+                                    const igraph_vector_t * weights,
+                                    void * options)
+{
+    return igraph_pagerank(graph,
+                           algo,
+                           vector,
+                           value,
+                           *vids,
+                           directed,
+                           damping,
+                           weights,
+                           options);
+}
+int __c2hs_wrapped__igraph_induced_subgraph(const igraph_t * graph,
+                                            igraph_t * res,
+                                            const igraph_vs_t * vids,
+                                            igraph_subgraph_implementation_t impl)
+{
+    return igraph_induced_subgraph(graph, res, *vids, impl);
+}
+int __c2hs_wrapped__igraph_closeness(const igraph_t * graph,
+                                     igraph_vector_t * res,
+                                     const igraph_vs_t * vids,
+                                     igraph_neimode_t mode,
+                                     const igraph_vector_t * weights,
+                                     igraph_bool_t normalized)
+{
+    return igraph_closeness(graph,
+                            res,
+                            *vids,
+                            mode,
+                            weights,
+                            normalized);
+}
+int __c2hs_wrapped__igraph_betweenness(const igraph_t * graph,
+                                       igraph_vector_t * res,
+                                       const igraph_vs_t * vids,
+                                       igraph_bool_t directed,
+                                       const igraph_vector_t * weights,
+                                       igraph_bool_t nobigint)
+{
+    return igraph_betweenness(graph,
+                              res,
+                              *vids,
+                              directed,
+                              weights,
+                              nobigint);
+}
diff --git a/cbits/haskelligraph.c b/cbits/haskelligraph.c
new file mode 100644
--- /dev/null
+++ b/cbits/haskelligraph.c
@@ -0,0 +1,42 @@
+#ifndef HASKELL_IGRAPH
+#define HASKELL_IGRAPH
+
+#include <igraph/igraph.h>
+
+igraph_integer_t igraph_get_eid_(igraph_t* graph, igraph_integer_t pfrom, igraph_integer_t pto,
+           igraph_bool_t directed, igraph_bool_t error)
+{
+  igraph_integer_t eid;
+  igraph_get_eid(graph, &eid, pfrom, pto, directed, error);
+  return eid;
+}
+
+char** igraph_strvector_get_(igraph_strvector_t* s, long int i)
+{
+  char** x = (char**) malloc (sizeof(char*));
+  igraph_strvector_get(s, i, x);
+  return x;
+}
+
+igraph_arpack_options_t* igraph_arpack_new()
+{
+  igraph_arpack_options_t *arpack = (igraph_arpack_options_t*) malloc(sizeof(igraph_arpack_options_t));
+  igraph_arpack_options_init(arpack);
+  return arpack;
+}
+
+void igraph_arpack_destroy(igraph_arpack_options_t* arpack)
+{
+  if (arpack)
+    free(arpack);
+  arpack = NULL;
+}
+
+void haskelligraph_init()
+{
+  /* attach attribute table */
+  igraph_i_set_attribute_table(&igraph_cattribute_table);
+}
+
+
+#endif
diff --git a/haskell-igraph.cabal b/haskell-igraph.cabal
new file mode 100644
--- /dev/null
+++ b/haskell-igraph.cabal
@@ -0,0 +1,97 @@
+-- Initial igraph-bindings.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                haskell-igraph
+version:             0.1.0
+synopsis:            Imcomplete igraph bindings
+description:         This is an attempt to create a complete bindings for the
+                     igraph<"http://igraph.org/c/"> library. Related work:
+                     https://hackage.haskell.org/package/igraph.
+license:             MIT
+license-file:        LICENSE
+author:              Kai Zhang
+maintainer:          kai@kzhang.org
+copyright:           (c) 2016 Kai Zhang
+category:            Math
+build-type:          Simple
+cabal-version:       >=1.22
+
+Flag graphics
+  Description: Enable graphics output
+  Default:     False
+
+library
+  exposed-modules:
+    IGraph.Internal.Initialization
+    IGraph.Internal.Constants
+    IGraph.Internal.Arpack
+    IGraph.Internal.Data
+    IGraph.Internal.Graph
+    IGraph.Internal.Attribute
+    IGraph.Internal.Isomorphism
+    IGraph.Internal.Selector
+    IGraph.Internal.Structure
+    IGraph.Internal.Clique
+    IGraph.Internal.Community
+    IGraph.Internal.Layout
+    IGraph
+    IGraph.Mutable
+    IGraph.Clique
+    IGraph.Structure
+    IGraph.Isomorphism
+    IGraph.Community
+    IGraph.Read
+    IGraph.Motif
+    IGraph.Layout
+    IGraph.Generators
+    IGraph.Exporter.GEXF
+
+  if flag(graphics)
+    exposed-modules: IGraph.Exporter.Graphics
+
+  if flag(graphics)
+    build-depends: diagrams-lib, diagrams-cairo
+
+  build-depends:
+      base >=4.0 && <5.0
+    , binary
+    , bytestring >=0.9
+    , bytestring-lexing >=0.5
+    , colour
+    , primitive
+    , unordered-containers
+    , hashable
+    , hxt
+    , split
+    , data-default-class
+
+  extra-libraries:     igraph
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  build-tools:         c2hs >=0.25.0
+  C-Sources:
+    cbits/haskelligraph.c
+    cbits/Selector.chs.c
+    cbits/Structure.chs.c
+
+test-suite tests
+  type: exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is: test.hs
+  other-modules:
+    Test.Basic
+    Test.Structure
+    Test.Utils
+
+  default-language:    Haskell2010
+  build-depends:
+      base
+    , haskell-igraph
+    , tasty
+    , tasty-golden
+    , tasty-hunit
+    , random
+
+source-repository  head
+  type: git
+  location: https://github.com/kaizhang/haskell-igraph.git
diff --git a/src/IGraph.hs b/src/IGraph.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph.hs
@@ -0,0 +1,232 @@
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+module IGraph
+    ( LGraph(..)
+    , U(..)
+    , D(..)
+    , Graph(..)
+    , mkGraph
+    , fromLabeledEdges
+
+    , unsafeFreeze
+    , freeze
+    , unsafeThaw
+    , thaw
+
+    , neighbors
+    , pre
+    , suc
+
+    , filterNode
+    , filterEdge
+
+    , nmap
+    , emap
+    ) where
+
+import           Control.Arrow             ((***))
+import           Control.Monad             (forM_, liftM)
+import           Control.Monad.Primitive
+import           Control.Monad.ST          (runST)
+import           Data.Hashable             (Hashable)
+import qualified Data.HashMap.Strict       as M
+import           Data.List                 (nub)
+import           Data.Maybe
+import Data.Binary
+import           System.IO.Unsafe          (unsafePerformIO)
+
+import           IGraph.Internal.Attribute
+import           IGraph.Internal.Constants
+import           IGraph.Internal.Graph
+import           IGraph.Internal.Selector
+import           IGraph.Mutable
+
+type Node = Int
+type Edge = (Node, Node)
+
+-- | graph with labeled nodes and edges
+data LGraph d v e = LGraph
+    { _graph       :: IGraphPtr
+    , _labelToNode :: M.HashMap v [Node]
+    }
+
+instance ( Binary v, Hashable v, Read v, Show v, Eq v
+         , Binary e, Read e, Show e, Graph d) => Binary (LGraph d v e) where
+    put gr = do
+        put nlabs
+        put es
+        put elabs
+      where
+        nlabs = map (nodeLab gr) $ nodes gr
+        es = edges gr
+        elabs = map (edgeLab gr) es
+    get = do
+        nlabs <- get
+        es <- get
+        elabs <- get
+        return $ mkGraph nlabs $ zip es elabs
+
+class MGraph d => Graph d where
+    isDirected :: LGraph d v e -> Bool
+    isD :: d -> Bool
+
+    nNodes :: LGraph d v e -> Int
+    nNodes (LGraph g _) = igraphVcount g
+    {-# INLINE nNodes #-}
+
+    nodes :: LGraph d v e -> [Int]
+    nodes gr = [0 .. nNodes gr - 1]
+    {-# INLINE nodes #-}
+
+    nEdges :: LGraph d v e -> Int
+    nEdges (LGraph g _) = igraphEcount g
+    {-# INLINE nEdges #-}
+
+    edges :: LGraph d v e -> [Edge]
+    edges gr@(LGraph g _) = unsafePerformIO $ mapM (igraphEdge g) [0..n-1]
+      where
+        n = nEdges gr
+    {-# INLINE edges #-}
+
+    hasEdge :: LGraph d v e -> Edge -> Bool
+    hasEdge (LGraph g _) (fr, to)
+        | igraphGetEid g fr to True False < 0 = False
+        | otherwise = True
+    {-# INLINE hasEdge #-}
+
+    nodeLab :: Read v => LGraph d v e -> Node -> v
+    nodeLab (LGraph g _) i = read $ igraphCattributeVAS g vertexAttr i
+    {-# INLINE nodeLab #-}
+
+    nodeLabMaybe :: Read v => LGraph d v e -> Node -> Maybe v
+    nodeLabMaybe gr@(LGraph g _) i =
+        if igraphCattributeHasAttr g IgraphAttributeVertex vertexAttr
+            then Just $ nodeLab gr i
+            else Nothing
+    {-# INLINE nodeLabMaybe #-}
+
+    getNodes :: (Hashable v, Eq v) => LGraph d v e -> v -> [Node]
+    getNodes gr x = M.lookupDefault [] x $ _labelToNode gr
+    {-# INLINE getNodes #-}
+
+    edgeLab :: Read e => LGraph d v e -> Edge -> e
+    edgeLab (LGraph g _) (fr,to) = read $ igraphCattributeEAS g edgeAttr $
+                                   igraphGetEid g fr to True True
+    {-# INLINE edgeLab #-}
+
+    edgeLabMaybe :: Read e => LGraph d v e -> Edge -> Maybe e
+    edgeLabMaybe gr@(LGraph g _) i =
+        if igraphCattributeHasAttr g IgraphAttributeEdge edgeAttr
+            then Just $ edgeLab gr i
+            else Nothing
+    {-# INLINE edgeLabMaybe #-}
+
+    edgeLabByEid :: Read e => LGraph d v e -> Int -> e
+    edgeLabByEid (LGraph g _) i = read $ igraphCattributeEAS g edgeAttr i
+    {-# INLINE edgeLabByEid #-}
+
+
+instance Graph U where
+    isDirected = const False
+    isD = const False
+
+instance Graph D where
+    isDirected = const True
+    isD = const True
+
+mkGraph :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
+        => [v] -> [(Edge, e)] -> LGraph d v e
+mkGraph vattr es = runST $ do
+    g <- new 0
+    addLNodes n vattr g
+    addLEdges (map (\((fr,to),x) -> (fr,to,x)) es) g
+    unsafeFreeze g
+  where
+    n = length vattr
+
+fromLabeledEdges :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
+                 => [((v, v), e)] -> LGraph d v e
+fromLabeledEdges es = mkGraph labels es'
+  where
+    es' = flip map es $ \((fr, to), x) -> ((f fr, f to), x)
+      where f x = M.lookupDefault undefined x labelToId
+    labels = nub $ concat [ [a,b] | ((a,b),_) <- es ]
+    labelToId = M.fromList $ zip labels [0..]
+
+unsafeFreeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
+unsafeFreeze (MLGraph g) = return $ LGraph g labToId
+  where
+    labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1]
+    nV = igraphVcount g
+    labels = map (read . igraphCattributeVAS g vertexAttr) [0 .. nV-1]
+
+freeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
+freeze (MLGraph g) = do
+    g' <- unsafePrimToPrim $ igraphCopy g
+    unsafeFreeze (MLGraph g')
+
+unsafeThaw :: PrimMonad m => LGraph d v e -> m (MLGraph (PrimState m) d v e)
+unsafeThaw (LGraph g _) = return $ MLGraph g
+
+thaw :: (PrimMonad m, Graph d) => LGraph d v e -> m (MLGraph (PrimState m) d v e)
+thaw (LGraph g _) = unsafePrimToPrim . liftM MLGraph . igraphCopy $ g
+
+-- | Find all neighbors of the given node
+neighbors :: LGraph d v e -> Node -> [Node]
+neighbors gr i = unsafePerformIO $ do
+    vs <- igraphVsAdj i IgraphAll
+    vit <- igraphVitNew (_graph gr) vs
+    vitToList vit
+
+-- | Find all nodes that have a link from the given node.
+suc :: LGraph D v e -> Node -> [Node]
+suc gr i = unsafePerformIO $ do
+    vs <- igraphVsAdj i IgraphOut
+    vit <- igraphVitNew (_graph gr) vs
+    vitToList vit
+
+-- | Find all nodes that link to to the given node.
+pre :: LGraph D v e -> Node -> [Node]
+pre gr i = unsafePerformIO $ do
+    vs <- igraphVsAdj i IgraphIn
+    vit <- igraphVitNew (_graph gr) vs
+    vitToList vit
+
+-- | Keep nodes that satisfy the constraint
+filterNode :: (Hashable v, Eq v, Read v, Graph d)
+           => (LGraph d v e -> Node -> Bool) -> LGraph d v e -> LGraph d v e
+filterNode f gr = runST $ do
+    let deleted = filter (not . f gr) $ nodes gr
+    gr' <- thaw gr
+    delNodes deleted gr'
+    unsafeFreeze gr'
+
+-- | Keep nodes that satisfy the constraint
+filterEdge :: (Hashable v, Eq v, Read v, Graph d)
+           => (LGraph d v e -> Edge -> Bool) -> LGraph d v e -> LGraph d v e
+filterEdge f gr = runST $ do
+    let deleted = filter (not . f gr) $ edges gr
+    gr' <- thaw gr
+    delEdges deleted gr'
+    unsafeFreeze gr'
+
+-- | Map a function over the node labels in a graph
+nmap :: (Graph d, Read v, Hashable u, Read u, Eq u, Show u)
+     => ((Node, v) -> u) -> LGraph d v e -> LGraph d u e
+nmap fn gr = unsafePerformIO $ do
+    (MLGraph g) <- thaw gr
+    forM_ (nodes gr) $ \i -> do
+        let label = fn (i, nodeLab gr i)
+        igraphCattributeVASSet g vertexAttr i (show label)
+    unsafeFreeze (MLGraph g)
+
+-- | Map a function over the edge labels in a graph
+emap :: (Graph d, Read v, Hashable v, Eq v, Read e1, Show e2)
+     => ((Edge, e1) -> e2) -> LGraph d v e1 -> LGraph d v e2
+emap fn gr = unsafePerformIO $ do
+    (MLGraph g) <- thaw gr
+    forM_ (edges gr) $ \(fr, to) -> do
+        let label = fn ((fr,to), edgeLabByEid gr i)
+            i = igraphGetEid g fr to True True
+        igraphCattributeEASSet g edgeAttr i (show label)
+    unsafeFreeze (MLGraph g)
diff --git a/src/IGraph/Clique.hs b/src/IGraph/Clique.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Clique.hs
@@ -0,0 +1,29 @@
+module IGraph.Clique
+    ( cliques
+    , maximalCliques
+    ) where
+
+import Control.Applicative ((<$>))
+import System.IO.Unsafe (unsafePerformIO)
+
+import IGraph
+import IGraph.Internal.Clique
+import IGraph.Internal.Data
+
+cliques :: LGraph d v e
+        -> (Int, Int)  -- ^ Minimum and maximum size of the cliques to be returned.
+                       -- No bound will be used if negative or zero
+        -> [[Int]]     -- ^ cliques represented by node ids
+cliques gr (lo, hi) = unsafePerformIO $ do
+    vpptr <- igraphVectorPtrNew 0
+    _ <- igraphCliques (_graph gr) vpptr lo hi
+    (map.map) truncate <$> vectorPPtrToList vpptr
+
+maximalCliques :: LGraph d v e
+               -> (Int, Int)  -- ^ Minimum and maximum size of the cliques to be returned.
+                              -- No bound will be used if negative or zero
+               -> [[Int]]     -- ^ cliques represented by node ids
+maximalCliques gr (lo, hi) = unsafePerformIO $ do
+    vpptr <- igraphVectorPtrNew 0
+    _ <- igraphMaximalCliques (_graph gr) vpptr lo hi
+    (map.map) truncate <$> vectorPPtrToList vpptr
diff --git a/src/IGraph/Community.hs b/src/IGraph/Community.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Community.hs
@@ -0,0 +1,73 @@
+module IGraph.Community
+    ( CommunityOpt(..)
+    , CommunityMethod(..)
+    , findCommunity
+    ) where
+
+import Control.Monad
+import Control.Applicative ((<$>))
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe (unsafePerformIO)
+import Data.List
+import Data.Ord
+import Data.Function (on)
+import Data.Default.Class
+
+import IGraph
+import IGraph.Mutable (U)
+import IGraph.Internal.Data
+import IGraph.Internal.Constants
+import IGraph.Internal.Community
+import IGraph.Internal.Arpack
+
+data CommunityOpt = CommunityOpt
+    { _method :: CommunityMethod
+    , _weights :: Maybe [Double]
+    , _nIter :: Int  -- ^ [LeadingEigenvector] number of iterations, default is 10000
+    , _nSpins :: Int  -- ^ [Spinglass] number of spins, default is 25
+    , _startTemp :: Double  -- ^ [Spinglass] the temperature at the start
+    , _stopTemp :: Double  -- ^ [Spinglass] the algorithm stops at this temperature
+    , _coolFact :: Double  -- ^ [Spinglass] the cooling factor for the simulated annealing
+    , _gamma :: Double  -- ^ [Spinglass] the gamma parameter of the algorithm. 
+    }
+
+data CommunityMethod = LeadingEigenvector
+                     | Spinglass
+
+instance Default CommunityOpt where
+    def = CommunityOpt
+        { _method = LeadingEigenvector
+        , _weights = Nothing
+        , _nIter = 10000
+        , _nSpins = 25
+        , _startTemp = 1.0
+        , _stopTemp = 0.01
+        , _coolFact = 0.99
+        , _gamma = 1.0
+        }
+
+findCommunity :: LGraph U v e -> CommunityOpt -> [[Int]]
+findCommunity gr opt = unsafePerformIO $ do
+    result <- igraphVectorNew 0
+    ws <- case _weights opt of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+
+    case _method opt of
+        LeadingEigenvector -> do
+            ap <- igraphArpackNew
+            igraphCommunityLeadingEigenvector (_graph gr) ws nullPtr result
+                                              (_nIter opt) ap nullPtr False
+                                              nullPtr nullPtr nullPtr
+                                              nullFunPtr nullPtr  
+        Spinglass ->
+            igraphCommunitySpinglass (_graph gr) ws nullPtr nullPtr result
+                                     nullPtr (_nSpins opt) False (_startTemp opt)
+                                     (_stopTemp opt) (_coolFact opt)
+                                     IgraphSpincommUpdateConfig (_gamma opt)
+                                     IgraphSpincommImpOrig 1.0
+
+    liftM ( map (fst . unzip) . groupBy ((==) `on` snd)
+          . sortBy (comparing snd) . zip [0..] ) $ vectorPtrToList result
+
diff --git a/src/IGraph/Exporter/GEXF.hs b/src/IGraph/Exporter/GEXF.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Exporter/GEXF.hs
@@ -0,0 +1,116 @@
+module IGraph.Exporter.GEXF
+    ( NodeAttr(..)
+    , defaultNodeAttributes
+    , EdgeAttr(..)
+    , defaultEdgeAttributes
+    , genXMLTree
+    , writeGEXF
+    ) where
+
+import Data.Hashable
+import Data.Colour (AlphaColour, black, over, alphaChannel, opaque)
+import Data.Colour.SRGB (toSRGB24, channelRed, channelBlue, channelGreen)
+import Text.XML.HXT.Core
+import Data.Tree.NTree.TypeDefs
+import Text.XML.HXT.DOM.TypeDefs
+import IGraph
+
+data NodeAttr = NodeAttr
+    { _size :: Double
+    , _nodeColour :: AlphaColour Double
+    , _nodeLabel :: String
+    , _positionX :: Double
+    , _positionY :: Double
+    , _nodeZindex :: Int
+    } deriving (Show, Read, Eq)
+
+instance Hashable NodeAttr where
+    hashWithSalt salt at = hashWithSalt salt $ _nodeLabel at
+
+defaultNodeAttributes :: NodeAttr
+defaultNodeAttributes = NodeAttr
+    { _size = 0.15
+    , _nodeColour = opaque black
+    , _nodeLabel = ""
+    , _positionX = 0
+    , _positionY = 0
+    , _nodeZindex = 1
+    }
+
+data EdgeAttr = EdgeAttr
+    { _edgeLabel :: String
+    , _edgeColour :: AlphaColour Double
+    , _edgeWeight :: Double
+    , _edgeArrowLength :: Double
+    , _edgeZindex :: Int
+    } deriving (Show, Read, Eq)
+
+instance Hashable EdgeAttr where
+    hashWithSalt salt at = hashWithSalt salt $ _edgeLabel at
+
+defaultEdgeAttributes :: EdgeAttr
+defaultEdgeAttributes = EdgeAttr
+    { _edgeLabel = ""
+    , _edgeColour = opaque black
+    , _edgeWeight = 1.0
+    , _edgeArrowLength = 5.0
+    , _edgeZindex = 0
+    }
+
+genXMLTree :: (ArrowXml a, Graph d) => LGraph d NodeAttr EdgeAttr -> a XmlTree XmlTree
+genXMLTree gr = root [] [gexf]
+  where
+    gexf = mkelem "gexf" [ attr "version" $ txt "1.2"
+                              , attr "xmlns" $ txt "http://www.gexf.net/1.2draft"
+                              , attr "xmlns:viz" $ txt "http://www.gexf.net/1.2draft/viz"
+                              , attr "xmlns:xsi" $ txt "http://www.w3.org/2001/XMLSchema-instance"
+                              , attr "xsi:schemaLocation" $ txt "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"
+                              ] [graph]
+    directed | isDirected gr = "directed"
+             | otherwise = "undirected"
+    graph = mkelem "graph" [ attr "mode" $ txt "static"
+                           , attr "defaultedgetype" $ txt directed
+                           ] [ns, es]
+    ns = mkelem "nodes" [] $ map mkNode $ nodes gr
+    es = mkelem "edges" [] $ map mkEdge $ edges gr
+    mkNode i =
+        mkelem "node" [ attr "id" $ txt $ show i
+                      , attr "label" $ txt $ _nodeLabel at ]
+                      [ aelem "viz:position" [ attr "x" $ txt $ show $ _positionX at
+                                             , attr "y" $ txt $ show $ _positionY at ]
+                      , aelem "viz:color" [ attr "r" $ txt r
+                                          , attr "g" $ txt g
+                                          , attr "b" $ txt b
+                                          , attr "a" $ txt a ]
+                      , aelem "viz:size" [attr "value" $ txt $ show $ _size at]
+                      ]
+      where
+        at = nodeLab gr i
+        rgb = toSRGB24 $ _nodeColour at `over` black
+        r = show (fromIntegral $ channelRed rgb :: Int)
+        b = show (fromIntegral $ channelBlue rgb :: Int)
+        g = show (fromIntegral $ channelGreen rgb :: Int)
+        a = show $ alphaChannel $ _nodeColour at
+
+    mkEdge (fr,to) =
+        mkelem "edge" [ attr "source" $ txt $ show fr
+                      , attr "target" $ txt $ show to
+                      , attr "weight" $ txt $ show $ _edgeWeight at ]
+                      [ aelem "viz:color" [ attr "r" $ txt r
+                                          , attr "g" $ txt g
+                                          , attr "b" $ txt b
+                                          , attr "a" $ txt a ]
+                      ]
+      where
+        at = edgeLab gr (fr,to)
+        rgb = toSRGB24 $ _edgeColour at `over` black
+        r = show (fromIntegral $ channelRed rgb :: Int)
+        b = show (fromIntegral $ channelBlue rgb :: Int)
+        g = show (fromIntegral $ channelGreen rgb :: Int)
+        a = show $ alphaChannel $ _edgeColour at
+{-# INLINE genXMLTree #-}
+
+writeGEXF :: Graph d => FilePath -> LGraph d NodeAttr EdgeAttr -> IO ()
+writeGEXF fl gr = runX (genXMLTree gr >>> writeDocument config fl) >> return ()
+  where
+    config = [withIndent yes]
diff --git a/src/IGraph/Exporter/Graphics.hs b/src/IGraph/Exporter/Graphics.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Exporter/Graphics.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs            #-}
+module IGraph.Exporter.Graphics
+    ( renderGraph
+    , graphToDiagram
+    ) where
+
+import           Data.List              (sortBy)
+import           Data.Ord               (comparing)
+import           Diagrams.Backend.Cairo
+import           Diagrams.Prelude
+import           Diagrams.Size          (dims)
+
+import           IGraph
+import           IGraph.Exporter.GEXF
+
+renderGraph :: Graph d => FilePath -> Double -> Double -> LGraph d NodeAttr EdgeAttr -> IO ()
+renderGraph out w h gr = renderCairo out (dims $ w ^& h) $ graphToDiagram gr
+
+graphToDiagram :: Graph d => LGraph d NodeAttr EdgeAttr -> Diagram B
+graphToDiagram gr = mconcat $ fst $ unzip $ sortBy (flip (comparing snd)) $
+    map drawNode (nodes gr) ++ map drawEdge (edges gr)
+  where
+    drawNode x = ( moveTo (_positionX nattr ^& _positionY nattr)
+                          (circle (_size nattr) # lwO 0 # fcA (_nodeColour nattr))
+                 , _nodeZindex nattr )
+      where
+        nattr = nodeLab gr x
+    drawEdge (from, to) = {-arrowBetween'
+        ( with & arrowTail .~ noTail
+               & arrowHead .~ arrowH
+               & headLength .~ output (_edgeArrowLength eattr)
+        ) start end-}
+        ( fromVertices [start, end]
+        # lwO (_edgeWeight eattr) # lcA (_edgeColour eattr), _edgeZindex eattr )
+      where
+        eattr = edgeLab gr (from, to)
+        start = _positionX nattr1 ^& _positionY nattr1
+        end = _positionX nattr2 ^& _positionY nattr2
+        nattr1 = nodeLab gr from
+        nattr2 = nodeLab gr to
+    --arrowH | isDirected gr = dart
+    --       | otherwise = noHead
+{-# INLINE graphToDiagram #-}
diff --git a/src/IGraph/Generators.hs b/src/IGraph/Generators.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Generators.hs
@@ -0,0 +1,28 @@
+module IGraph.Generators
+    ( ErdosRenyiModel(..)
+    , erdosRenyiGame
+    ) where
+
+import IGraph
+import IGraph.Mutable
+import IGraph.Internal.Graph
+import IGraph.Internal.Constants
+import IGraph.Internal.Initialization
+
+data ErdosRenyiModel = GNP
+                     | GNM
+
+erdosRenyiGame :: Graph d
+               => ErdosRenyiModel
+               -> Int   -- ^ n
+               -> Double   -- ^ p or m
+               -> d     -- ^ directed
+               -> Bool  -- ^ self-loop
+               -> IO (LGraph d () ())
+erdosRenyiGame model n p_or_m d self = do
+    gp <- igraphInit >> igraphErdosRenyiGame model' n p_or_m (isD d) self
+    unsafeFreeze $ MLGraph gp
+  where
+    model' = case model of
+        GNP -> IgraphErdosRenyiGnp
+        GNM -> IgraphErdosRenyiGnm
diff --git a/src/IGraph/Internal/Arpack.chs b/src/IGraph/Internal/Arpack.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Arpack.chs
@@ -0,0 +1,12 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Arpack where
+
+import Control.Monad
+import Foreign
+import Foreign.C.Types
+
+#include "cbits/haskelligraph.c"
+
+{#pointer *igraph_arpack_options_t as ArpackOptPtr foreign finalizer igraph_arpack_destroy newtype#}
+
+{#fun igraph_arpack_new as ^ { } -> `ArpackOptPtr' #}
diff --git a/src/IGraph/Internal/Attribute.chs b/src/IGraph/Internal/Attribute.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Attribute.chs
@@ -0,0 +1,60 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Attribute where
+
+import qualified Data.ByteString.Char8 as B
+import Control.Monad
+import Control.Applicative
+import Foreign
+import Foreign.C.Types
+import Foreign.C.String
+import System.IO.Unsafe (unsafePerformIO)
+
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+{#import IGraph.Internal.Constants #}
+
+#include "igraph/igraph.h"
+
+makeAttributeRecord :: Show a
+                    => String    -- ^ name of the attribute
+                    -> [a]       -- ^ values of the attribute
+                    -> AttributeRecord
+makeAttributeRecord name xs = unsafePerformIO $ do
+    ptr <- newCAString name
+    value <- listToStrVector $ map (B.pack . show) xs
+    return $ AttributeRecord ptr 2 value
+
+data AttributeRecord = AttributeRecord CString Int StrVectorPtr
+
+instance Storable AttributeRecord where
+    sizeOf _ = {#sizeof igraph_attribute_record_t #}
+    alignment _ = {#alignof igraph_attribute_record_t #}
+    peek p = AttributeRecord
+        <$> ({#get igraph_attribute_record_t->name #} p)
+        <*> liftM fromIntegral ({#get igraph_attribute_record_t->type #} p)
+        <*> ( do ptr <- {#get igraph_attribute_record_t->value #} p
+                 fptr <- newForeignPtr_ . castPtr $ ptr
+                 return $ StrVectorPtr fptr )
+    poke p (AttributeRecord name t vptr) = do
+        {#set igraph_attribute_record_t.name #} p name
+        {#set igraph_attribute_record_t.type #} p $ fromIntegral t
+        withStrVectorPtr vptr $ \ptr ->
+            {#set igraph_attribute_record_t.value #} p $ castPtr ptr
+
+{#fun pure igraph_cattribute_has_attr as ^ { `IGraphPtr', `AttributeElemtype', `String' } -> `Bool' #}
+
+{#fun igraph_cattribute_GAN_set as ^ { `IGraphPtr', `String', `Double' } -> `Int' #}
+
+{#fun pure igraph_cattribute_GAN as ^ { `IGraphPtr', `String' } -> `Double' #}
+
+{#fun pure igraph_cattribute_VAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #}
+
+{#fun pure igraph_cattribute_EAN as ^ { `IGraphPtr', `String', `Int' } -> `Double' #}
+
+{#fun pure igraph_cattribute_EAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #}
+
+{#fun igraph_cattribute_EAS_setv as ^ { `IGraphPtr', `String', `StrVectorPtr' } -> `Int' #}
+
+{#fun igraph_cattribute_VAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #}
+
+{#fun igraph_cattribute_EAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #}
diff --git a/src/IGraph/Internal/Clique.chs b/src/IGraph/Internal/Clique.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Clique.chs
@@ -0,0 +1,17 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Clique where
+
+import qualified Foreign.Marshal.Utils as C2HSImp
+import qualified Foreign.Ptr as C2HSImp
+
+import Foreign
+import Foreign.C.Types
+
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+
+#include "cbits/haskelligraph.c"
+
+{#fun igraph_cliques as ^ { `IGraphPtr', `VectorPPtr', `Int', `Int' } -> `Int' #}
+
+{#fun igraph_maximal_cliques as ^ { `IGraphPtr', `VectorPPtr', `Int', `Int' } -> `Int' #}
diff --git a/src/IGraph/Internal/Community.chs b/src/IGraph/Internal/Community.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Community.chs
@@ -0,0 +1,55 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Community where
+
+import Foreign
+import Foreign.C.Types
+
+{#import IGraph.Internal.Arpack #}
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+{#import IGraph.Internal.Constants #}
+
+#include "cbits/haskelligraph.c"
+
+{#fun igraph_community_spinglass as ^
+{ `IGraphPtr'
+, `VectorPtr'
+, id `Ptr CDouble'
+, id `Ptr CDouble'
+, `VectorPtr'
+, id `Ptr VectorPtr'
+, `Int'
+, `Bool'
+, `Double'
+, `Double'
+, `Double'
+, `SpincommUpdate'
+, `Double'
+, `SpinglassImplementation'
+, `Double'
+} -> `Int' #}
+
+{#fun igraph_community_leading_eigenvector as ^
+{ `IGraphPtr'
+, `VectorPtr'
+, id `Ptr MatrixPtr'
+, `VectorPtr'
+, `Int'
+, `ArpackOptPtr'
+, id `Ptr CDouble'
+, `Bool'
+, id `Ptr VectorPtr'
+, id `Ptr VectorPPtr'
+, id `Ptr VectorPtr'
+, id `T'
+, id `Ptr ()'
+} -> `Int' #}
+
+type T = FunPtr ( Ptr VectorPtr
+                -> CLong
+                -> CDouble
+                -> Ptr VectorPtr
+                -> FunPtr (Ptr CDouble -> Ptr CDouble -> CInt -> Ptr () -> IO CInt)
+                -> Ptr ()
+                -> Ptr ()
+                -> IO CInt)
diff --git a/src/IGraph/Internal/Constants.chs b/src/IGraph/Internal/Constants.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Constants.chs
@@ -0,0 +1,30 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Constants where
+
+import Foreign
+
+#include "cbits/haskelligraph.c"
+
+{#enum igraph_neimode_t as Neimode {underscoreToCase}
+    deriving (Show, Eq) #}
+
+{#enum igraph_edgeorder_type_t as EdgeOrderType {underscoreToCase}
+    deriving (Show, Eq) #}
+
+{#enum igraph_spincomm_update_t as SpincommUpdate {underscoreToCase}
+    deriving (Show, Eq) #}
+
+{#enum igraph_spinglass_implementation_t as SpinglassImplementation {underscoreToCase}
+    deriving (Show, Eq) #}
+
+{#enum igraph_attribute_elemtype_t as AttributeElemtype {underscoreToCase}
+    deriving (Show, Eq) #}
+
+{#enum igraph_subgraph_implementation_t as SubgraphImplementation {underscoreToCase}
+    deriving (Show, Read, Eq) #}
+
+{#enum igraph_pagerank_algo_t as PagerankAlgo {underscoreToCase}
+    deriving (Show, Read, Eq) #}
+
+{#enum igraph_erdos_renyi_t as ErdosRenyi {underscoreToCase}
+    deriving (Show, Read, Eq) #}
diff --git a/src/IGraph/Internal/Data.chs b/src/IGraph/Internal/Data.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Data.chs
@@ -0,0 +1,149 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Data where
+
+import Control.Monad
+import qualified Data.ByteString.Char8 as B
+import Foreign
+import Foreign.C.Types
+import Foreign.C.String
+import System.IO.Unsafe (unsafePerformIO)
+import Data.List (transpose)
+import Data.List.Split (chunksOf)
+
+#include "cbits/haskelligraph.c"
+
+{#pointer *igraph_vector_t as VectorPtr foreign finalizer igraph_vector_destroy newtype#}
+
+-- Construtors and destructors
+
+{#fun igraph_vector_init as igraphVectorNew { +, `Int' } -> `VectorPtr' #}
+
+listToVector :: [Double] -> IO VectorPtr
+listToVector xs = do
+    vec <- igraphVectorNew n
+    forM_ (zip [0..] xs) $ \(i,x) -> igraphVectorSet vec i x
+    return vec
+  where
+    n = length xs
+
+vectorPtrToList :: VectorPtr -> IO [Double]
+vectorPtrToList vptr = do
+    n <- igraphVectorSize vptr
+    allocaArray n $ \ptr -> do
+        igraphVectorCopyTo vptr ptr
+        liftM (map realToFrac) $ peekArray n ptr
+
+-- Initializing elements
+
+{#fun igraph_vector_null as ^ { `VectorPtr' } -> `()' #}
+
+{#fun igraph_vector_fill as ^ { `VectorPtr', `Double' } -> `()' #}
+
+
+-- Accessing elements
+
+{#fun pure igraph_vector_e as ^ { `VectorPtr', `Int' } -> `Double' #}
+
+{#fun igraph_vector_set as ^ { `VectorPtr', `Int', `Double' } -> `()' #}
+
+{#fun pure igraph_vector_tail as ^ { `VectorPtr' } -> `Double' #}
+
+
+-- Copying vectors
+
+{#fun igraph_vector_copy_to as ^ { `VectorPtr', id `Ptr CDouble' } -> `()' #}
+
+-- Vector properties
+{#fun igraph_vector_size as ^ { `VectorPtr' } -> `Int' #}
+
+
+{#pointer *igraph_vector_ptr_t as VectorPPtr foreign finalizer igraph_vector_ptr_destroy_all newtype#}
+
+{#fun igraph_vector_ptr_init as igraphVectorPtrNew { +, `Int' } -> `VectorPPtr' #}
+
+{#fun igraph_vector_ptr_e as ^ { `VectorPPtr', `Int' } -> `Ptr ()' #}
+{#fun igraph_vector_ptr_set as ^ { `VectorPPtr', `Int', id `Ptr ()' } -> `()' #}
+{#fun igraph_vector_ptr_size as ^ { `VectorPPtr' } -> `Int' #}
+
+listToVectorP :: [Ptr ()] -> IO VectorPPtr
+listToVectorP xs = do
+    vptr <- igraphVectorPtrNew n
+    forM_ (zip [0..] xs) $ \(i,x) -> igraphVectorPtrSet vptr i x
+    return vptr
+  where
+    n = length xs
+
+vectorPPtrToList :: VectorPPtr -> IO [[Double]]
+vectorPPtrToList vpptr = do
+    n <- igraphVectorPtrSize vpptr
+    forM [0..n-1] $ \i -> do
+        vptr <- igraphVectorPtrE vpptr i
+        fptr <- newForeignPtr_ $ castPtr vptr
+        vectorPtrToList $ VectorPtr fptr
+
+
+{#pointer *igraph_strvector_t as StrVectorPtr foreign finalizer igraph_strvector_destroy newtype#}
+
+{#fun igraph_strvector_init as igraphStrvectorNew { +, `Int' } -> `StrVectorPtr' #}
+
+{#fun igraph_strvector_get_ as igraphStrvectorGet' { `StrVectorPtr', `Int' } -> `Ptr CString' id #}
+
+igraphStrvectorGet :: StrVectorPtr -> Int -> String
+igraphStrvectorGet vec i = unsafePerformIO $ do
+    ptr <- igraphStrvectorGet' vec i
+    peek ptr >>= peekCString
+
+{#fun igraph_strvector_set as ^ { `StrVectorPtr', `Int', id `CString'} -> `()' #}
+{#fun igraph_strvector_set2 as ^ { `StrVectorPtr', `Int', id `CString', `Int'} -> `()' #}
+
+listToStrVector :: [B.ByteString] -> IO StrVectorPtr
+listToStrVector xs = do
+    vec <- igraphStrvectorNew n
+    forM_ (zip [0..] xs) $ \(i,x) -> B.useAsCString x (igraphStrvectorSet vec i)
+    return vec
+  where
+    n = length xs
+
+
+{#pointer *igraph_matrix_t as MatrixPtr foreign finalizer igraph_matrix_destroy newtype#}
+
+{#fun igraph_matrix_init as igraphMatrixNew { +, `Int', `Int' } -> `MatrixPtr' #}
+
+{#fun igraph_matrix_null as ^ { `MatrixPtr' } -> `()' #}
+
+{#fun igraph_matrix_fill as ^ { `MatrixPtr', `Double' } -> `()' #}
+
+{#fun igraph_matrix_e as ^ { `MatrixPtr', `Int', `Int' } -> `Double' #}
+
+{#fun igraph_matrix_set as ^ { `MatrixPtr', `Int', `Int', `Double' } -> `()' #}
+
+{#fun igraph_matrix_copy_to as ^ { `MatrixPtr', id `Ptr CDouble' } -> `()' #}
+
+{#fun igraph_matrix_nrow as ^ { `MatrixPtr' } -> `Int' #}
+
+{#fun igraph_matrix_ncol as ^ { `MatrixPtr' } -> `Int' #}
+
+-- row lists to matrix
+listsToMatrixPtr :: [[Double]] -> IO MatrixPtr
+listsToMatrixPtr xs = do
+    mptr <- igraphMatrixNew r c
+    forM_ (zip [0..] xs) $ \(i, row) ->
+        forM_ (zip [0..] row) $ \(j,v) ->
+            igraphMatrixSet mptr i j v
+    return mptr
+  where
+    r = length xs
+    c = maximum $ map length xs
+
+-- to row lists
+matrixPtrToLists :: MatrixPtr -> IO [[Double]]
+matrixPtrToLists = liftM transpose . matrixPtrToColumnLists
+
+matrixPtrToColumnLists :: MatrixPtr -> IO [[Double]]
+matrixPtrToColumnLists mptr = do
+    r <- igraphMatrixNrow mptr
+    c <- igraphMatrixNcol mptr
+    xs <- allocaArray (r*c) $ \ptr -> do
+        igraphMatrixCopyTo mptr ptr
+        peekArray (r*c) ptr
+    return $ chunksOf r $ map realToFrac xs
diff --git a/src/IGraph/Internal/Graph.chs b/src/IGraph/Internal/Graph.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Graph.chs
@@ -0,0 +1,56 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Graph where
+
+import Control.Monad
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe (unsafePerformIO)
+
+{#import IGraph.Internal.Initialization #}
+{#import IGraph.Internal.Data #}
+{#import IGraph.Internal.Constants #}
+
+#include "cbits/haskelligraph.c"
+
+{#pointer *igraph_t as IGraphPtr foreign finalizer igraph_destroy newtype#}
+
+-- | create a igraph object and attach a finalizer
+igraphNew :: Int -> Bool -> HasInit -> IO IGraphPtr
+igraphNew n directed _ = igraphNew' n directed
+
+-- Graph Constructors and Destructors
+
+{#fun igraph_empty as igraphNew' { +, `Int', `Bool' } -> `IGraphPtr' #}
+
+{#fun igraph_copy as ^ { +, `IGraphPtr' } -> `IGraphPtr' #}
+
+-- Basic Query Operations
+
+{#fun pure igraph_vcount as ^ { `IGraphPtr' } -> `Int' #}
+
+{#fun pure igraph_ecount as ^ { `IGraphPtr' } -> `Int' #}
+
+{#fun pure igraph_get_eid_ as igraphGetEid { `IGraphPtr', `Int', `Int', `Bool', `Bool' } -> `Int' #}
+
+{#fun igraph_edge as igraphEdge' { `IGraphPtr', `Int', id `Ptr CInt', id `Ptr CInt' } -> `Int' #}
+igraphEdge :: IGraphPtr -> Int -> IO (Int, Int)
+igraphEdge g i = alloca $ \fr -> alloca $ \to -> do
+    igraphEdge' g i fr to
+    fr' <- peek fr
+    to' <- peek to
+    return (fromIntegral fr', fromIntegral to')
+
+-- Adding and Deleting Vertices and Edges
+
+{# fun igraph_add_vertices as ^ { `IGraphPtr', `Int', id `Ptr ()' } -> `()' #}
+
+{# fun igraph_add_edge as ^ { `IGraphPtr', `Int', `Int' } -> `()' #}
+
+{# fun igraph_add_edges as ^ { `IGraphPtr', `VectorPtr', id `Ptr ()' } -> `()' #}
+
+
+-- generators
+
+{#fun igraph_full as ^ { +, `Int', `Bool', `Bool' } -> `IGraphPtr' #}
+
+{#fun igraph_erdos_renyi_game as ^ {+, `ErdosRenyi', `Int', `Double', `Bool', `Bool'} -> `IGraphPtr' #}
diff --git a/src/IGraph/Internal/Initialization.chs b/src/IGraph/Internal/Initialization.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Initialization.chs
@@ -0,0 +1,12 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Initialization where
+
+#include "cbits/haskelligraph.c"
+
+data HasInit
+
+igraphInit :: IO HasInit
+igraphInit = do haskelligraphInit
+                return undefined
+
+{#fun haskelligraph_init as ^ {} -> `()' #}
diff --git a/src/IGraph/Internal/Isomorphism.chs b/src/IGraph/Internal/Isomorphism.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Isomorphism.chs
@@ -0,0 +1,16 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Isomorphism where
+
+import Foreign
+import Foreign.C.Types
+
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+
+#include "igraph/igraph.h"
+
+{#fun igraph_get_subisomorphisms_vf2 as ^ { `IGraphPtr', `IGraphPtr',
+    id `Ptr ()', id `Ptr ()', id `Ptr ()', id `Ptr ()', `VectorPPtr',
+    id `FunPtr (Ptr IGraphPtr -> Ptr IGraphPtr -> CInt -> CInt -> Ptr () -> IO CInt)',
+    id `FunPtr (Ptr IGraphPtr -> Ptr IGraphPtr -> CInt -> CInt -> Ptr () -> IO CInt)',
+    id `Ptr ()'} -> `Int' #}
diff --git a/src/IGraph/Internal/Layout.chs b/src/IGraph/Internal/Layout.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Layout.chs
@@ -0,0 +1,38 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Layout where
+
+import qualified Foreign.Marshal.Utils as C2HSImp
+import qualified Foreign.Ptr as C2HSImp
+
+import Foreign
+import Foreign.C.Types
+
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+
+#include "igraph/igraph.h"
+
+{#fun igraph_layout_kamada_kawai as ^ { `IGraphPtr'
+, `MatrixPtr'
+, `Int'
+, `Double'
+, `Double'
+, `Double'
+, `Double'
+, `Bool'
+, id `Ptr VectorPtr'
+, id `Ptr VectorPtr'
+, id `Ptr VectorPtr'
+, id `Ptr VectorPtr'
+} -> `Int' #}
+
+{# fun igraph_layout_lgl as ^ { `IGraphPtr'
+, `MatrixPtr'
+, `Int'
+, `Double'
+, `Double'
+, `Double'
+, `Double'
+, `Double'
+, `Int'
+} -> `Int' #}
diff --git a/src/IGraph/Internal/Selector.chs b/src/IGraph/Internal/Selector.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Selector.chs
@@ -0,0 +1,127 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Selector where
+
+import Control.Monad
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe (unsafePerformIO)
+
+{#import IGraph.Internal.Constants #}
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Data #}
+
+#include "igraph/igraph.h"
+
+{#pointer *igraph_vs_t as IGraphVsPtr foreign finalizer igraph_vs_destroy newtype #}
+
+{#fun igraph_vs_all as ^ { + } -> `IGraphVsPtr' #}
+
+{#fun igraph_vs_adj as ^ { +, `Int', `Neimode' } -> `IGraphVsPtr' #}
+
+{#fun igraph_vs_vector as ^ { +, `VectorPtr' } -> `IGraphVsPtr' #}
+
+
+-- Vertex iterator
+
+{#pointer *igraph_vit_t as IGraphVitPtr foreign finalizer igraph_vit_destroy newtype #}
+
+#c
+igraph_vit_t* igraph_vit_new(const igraph_t *graph, igraph_vs_t vs) {
+  igraph_vit_t* vit = (igraph_vit_t*) malloc (sizeof (igraph_vit_t));
+  igraph_vit_create(graph, vs, vit);
+  return vit;
+}
+
+igraph_bool_t igraph_vit_end(igraph_vit_t *vit) {
+  return IGRAPH_VIT_END(*vit);
+}
+
+void igraph_vit_next(igraph_vit_t *vit) {
+  IGRAPH_VIT_NEXT(*vit);
+}
+
+igraph_integer_t igraph_vit_get(igraph_vit_t *vit) {
+  return IGRAPH_VIT_GET(*vit);
+}
+#endc
+
+{#fun igraph_vit_new as ^ { `IGraphPtr', %`IGraphVsPtr' } -> `IGraphVitPtr' #}
+
+{#fun igraph_vit_end as ^ { `IGraphVitPtr' } -> `Bool' #}
+
+{#fun igraph_vit_next as ^ { `IGraphVitPtr' } -> `()' #}
+
+{#fun igraph_vit_get as ^ { `IGraphVitPtr' } -> `Int' #}
+
+vitToList :: IGraphVitPtr -> IO [Int]
+vitToList vit = do
+    isEnd <- igraphVitEnd vit
+    if isEnd
+      then return []
+      else do
+        cur <- igraphVitGet vit
+        igraphVitNext vit
+        acc <- vitToList vit
+        return $ cur : acc
+
+
+-- Edge Selector
+
+{#pointer *igraph_es_t as IGraphEsPtr foreign finalizer igraph_es_destroy newtype #}
+
+{#fun igraph_es_all as ^ { +, `EdgeOrderType' } -> `IGraphEsPtr' #}
+
+{# fun igraph_es_vector as ^ { +, `VectorPtr' } -> `IGraphEsPtr' #}
+
+
+-- Edge iterator
+
+{#pointer *igraph_eit_t as IGraphEitPtr foreign finalizer igraph_eit_destroy newtype #}
+
+#c
+igraph_eit_t* igraph_eit_new(const igraph_t *graph, igraph_es_t es) {
+  igraph_eit_t* eit = (igraph_eit_t*) malloc (sizeof (igraph_eit_t));
+  igraph_eit_create(graph, es, eit);
+  return eit;
+}
+
+igraph_bool_t igraph_eit_end(igraph_eit_t *eit) {
+  return IGRAPH_EIT_END(*eit);
+}
+
+void igraph_eit_next(igraph_eit_t *eit) {
+  IGRAPH_EIT_NEXT(*eit);
+}
+
+igraph_integer_t igraph_eit_get(igraph_eit_t *eit) {
+  return IGRAPH_EIT_GET(*eit);
+}
+#endc
+
+{#fun igraph_eit_new as ^ { `IGraphPtr', %`IGraphEsPtr' } -> `IGraphEitPtr' #}
+
+{#fun igraph_eit_end as ^ { `IGraphEitPtr' } -> `Bool' #}
+
+{#fun igraph_eit_next as ^ { `IGraphEitPtr' } -> `()' #}
+
+{#fun igraph_eit_get as ^ { `IGraphEitPtr' } -> `Int' #}
+
+eitToList :: IGraphEitPtr -> IO [Int]
+eitToList eit = do
+    isEnd <- igraphEitEnd eit
+    if isEnd
+      then return []
+      else do
+        cur <- igraphEitGet eit
+        igraphEitNext eit
+        acc <- eitToList eit
+        return $ cur : acc
+
+-- delete vertices
+
+{# fun igraph_delete_vertices as ^ { `IGraphPtr', %`IGraphVsPtr' } -> `Int' #}
+
+
+-- delete edges
+
+{# fun igraph_delete_edges as ^ { `IGraphPtr', %`IGraphEsPtr' } -> `Int' #}
diff --git a/src/IGraph/Internal/Structure.chs b/src/IGraph/Internal/Structure.chs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/Structure.chs
@@ -0,0 +1,61 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module IGraph.Internal.Structure where
+
+import Foreign
+import Foreign.C.Types
+
+{#import IGraph.Internal.Graph #}
+{#import IGraph.Internal.Selector #}
+{#import IGraph.Internal.Constants #}
+{#import IGraph.Internal.Data #}
+{#import IGraph.Internal.Arpack #}
+
+#include "igraph/igraph.h"
+
+{#fun igraph_induced_subgraph as ^ { `IGraphPtr'
+                                   , id `Ptr (IGraphPtr)'
+                                   , %`IGraphVsPtr'
+                                   , `SubgraphImplementation' } -> `Int' #}
+
+{#fun igraph_closeness as ^ { `IGraphPtr'
+                            , `VectorPtr'
+                            , %`IGraphVsPtr'
+                            , `Neimode'
+                            , `VectorPtr'
+                            , `Bool' } -> `Int' #}
+
+{#fun igraph_betweenness as ^ { `IGraphPtr'
+                              , `VectorPtr'
+                              , %`IGraphVsPtr'
+                              , `Bool'
+                              , `VectorPtr'
+                              , `Bool' } -> `Int' #}
+
+{#fun igraph_eigenvector_centrality as ^ { `IGraphPtr'
+                                         , `VectorPtr'
+                                         , id `Ptr CDouble'
+                                         , `Bool'
+                                         , `Bool'
+                                         , `VectorPtr'
+                                         , `ArpackOptPtr' } -> `Int' #}
+
+{#fun igraph_pagerank as ^ { `IGraphPtr'
+                           , `PagerankAlgo'
+                           , `VectorPtr'
+                           , id `Ptr CDouble'
+                           , %`IGraphVsPtr'
+                           , `Bool'
+                           , `Double'
+                           , `VectorPtr'
+                           , id `Ptr ()' } -> `Int' #}
+
+{#fun igraph_personalized_pagerank as ^ { `IGraphPtr'
+                                        , `PagerankAlgo'
+                                        , `VectorPtr'
+                                        , id `Ptr CDouble'
+                                        , %`IGraphVsPtr'
+                                        , `Bool'
+                                        , `Double'
+                                        , `VectorPtr'
+                                        , `VectorPtr'
+                                        , id `Ptr ()' } -> `Int' #}
diff --git a/src/IGraph/Isomorphism.hs b/src/IGraph/Isomorphism.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Isomorphism.hs
@@ -0,0 +1,23 @@
+module IGraph.Isomorphism (getSubisomorphisms) where
+
+import           Foreign
+import           Foreign.C.Types
+import           System.IO.Unsafe            (unsafePerformIO)
+
+import           IGraph
+import           IGraph.Internal.Data
+import           IGraph.Internal.Isomorphism
+
+getSubisomorphisms :: Graph d
+                   => LGraph d v1 e1  -- ^ graph to be searched in
+                   -> LGraph d v2 e2   -- ^ smaller graph
+                   -> [[Int]]
+getSubisomorphisms g1 g2 = unsafePerformIO $ do
+    vpptr <- igraphVectorPtrNew 0
+    igraphGetSubisomorphismsVf2 gptr1 gptr2 nullPtr nullPtr nullPtr nullPtr vpptr
+        nullFunPtr nullFunPtr nullPtr
+    (map.map) truncate <$> vectorPPtrToList vpptr
+  where
+    gptr1 = _graph g1
+    gptr2 = _graph g2
+{-# INLINE getSubisomorphisms #-}
diff --git a/src/IGraph/Layout.hs b/src/IGraph/Layout.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Layout.hs
@@ -0,0 +1,86 @@
+module IGraph.Layout
+    ( getLayout
+    , LayoutMethod(..)
+    , defaultKamadaKawai
+    , defaultLGL
+    ) where
+
+import           Control.Applicative    ((<$>))
+import           Data.Default.Class
+import           Data.Maybe             (isJust)
+import           Foreign                (nullPtr)
+import           System.IO.Unsafe       (unsafePerformIO)
+
+import           IGraph
+import           IGraph.Internal.Clique
+import           IGraph.Internal.Data
+import           IGraph.Internal.Layout
+
+data LayoutMethod = KamadaKawai
+    { kk_seed      :: !(Maybe [(Double, Double)])
+    , kk_nIter     :: !Int
+    , kk_sigma     :: (Int -> Double) -- ^ The base standard deviation of position
+                                  -- change proposals
+    , kk_startTemp :: !Double  -- ^ The initial temperature for the annealing
+    , kk_coolFact  :: !Double  -- ^ The cooling factor for the simulated annealing
+    , kk_const     :: (Int -> Double)  -- ^ The Kamada-Kawai vertex attraction constant
+    }
+                  | LGL   -- ^ the Large Graph Layout algorithm
+    { lgl_nIter      :: !Int
+    , lgl_maxdelta   :: (Int -> Double)  -- ^ The maximum length of the move allowed for
+                               -- a vertex in a single iteration.
+                               -- A reasonable default is the number of vertices.
+    , lgl_area       :: (Int -> Double)  -- ^ This parameter gives the area of the square
+                           -- on which the vertices will be placed. A reasonable
+                           -- default value is the number of vertices squared.
+    , lgl_coolexp    :: !Double  -- ^ The cooling exponent. A reasonable default value is 1.5.
+    , lgl_repulserad :: (Int -> Double) -- ^ Determines the radius at which vertex-vertex repulsion cancels out attraction of adjacent vertices. A reasonable default value is area times the number of vertices.
+    , lgl_cellsize   :: (Int -> Double)
+    }
+
+defaultKamadaKawai :: LayoutMethod
+defaultKamadaKawai = KamadaKawai
+    { kk_seed = Nothing
+    , kk_nIter = 1000
+    , kk_sigma = \x -> fromIntegral x / 4
+    , kk_startTemp = 10
+    , kk_coolFact = 0.99
+    , kk_const = \x -> fromIntegral $ x^2
+    }
+
+defaultLGL :: LayoutMethod
+defaultLGL = LGL
+    { lgl_nIter = 150
+    , lgl_maxdelta = \x -> fromIntegral x
+    , lgl_area = area
+    , lgl_coolexp = 1.5
+    , lgl_repulserad = \x -> fromIntegral x * area x
+    , lgl_cellsize = \x -> area x ** 0.25
+    }
+  where
+    area x = fromIntegral $ x^2
+
+getLayout :: Graph d => LGraph d v e -> LayoutMethod -> IO [(Double, Double)]
+getLayout gr method = do
+    case method of
+        KamadaKawai seed niter sigma initemp coolexp kkconst -> do
+            mptr <- case seed of
+                Nothing -> igraphMatrixNew 0 0
+                Just xs -> if length xs /= nNodes gr
+                               then error "Seed error: incorrect size"
+                               else listsToMatrixPtr $ (\(x,y) -> [x,y]) $ unzip xs
+
+            igraphLayoutKamadaKawai gptr mptr niter (sigma n) initemp coolexp
+                (kkconst n) (isJust seed) nullPtr nullPtr nullPtr nullPtr
+            [x, y] <- matrixPtrToColumnLists mptr
+            return $ zip x y
+
+        LGL niter delta area coolexp repulserad cellsize -> do
+            mptr <- igraphMatrixNew 0 0
+            igraphLayoutLgl gptr mptr niter (delta n) (area n) coolexp
+                (repulserad n) (cellsize n) (-1)
+            [x, y] <- matrixPtrToColumnLists mptr
+            return $ zip x y
+  where
+    n = nNodes gr
+    gptr = _graph gr
diff --git a/src/IGraph/Motif.hs b/src/IGraph/Motif.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Motif.hs
@@ -0,0 +1,45 @@
+module IGraph.Motif
+    (triad) where
+
+import IGraph
+
+-- | Every triple of vertices in a directed graph
+-- 003: A, B, C, the empty graph.
+-- 012: A->B, C, a graph with a single directed edge.
+-- 102: A<->B, C, a graph with a mutual connection between two vertices.
+-- 021D: A<-B->C, the binary out-tree.
+-- 021U: A->B<-C, the binary in-tree.
+-- 021C: A->B->C, the directed line.
+-- 111D: A<->B<-C.
+-- 111U: A<->B->C.
+-- 030T: A->B<-C, A->C.
+-- 030C: A<-B<-C, A->C.
+-- 201: A<->B<->C.
+-- 120D: A<-B->C, A<->C.
+-- 120U: A->B<-C, A<->C.
+-- 120C: A->B->C, A<->C.
+-- 210: A->B<->C, A<->C.
+-- 300: A<->B<->C, A<->C, the complete graph.
+triad :: [LGraph D () ()]
+triad = map make xs
+  where
+    xs = [ []
+         , [(0,1)]
+         , [(0,1), (1,0)]
+         , [(1,0), (1,2)]
+         , [(0,1), (2,1)]
+         , [(0,1), (1,2)]
+         , [(0,1), (1,0), (2,1)]
+         , [(0,1), (1,0), (1,2)]
+         , [(0,1), (2,1), (0,2)]
+         , [(1,0), (2,1), (0,2)]
+         , [(0,1), (1,0), (0,2), (2,0)]
+         , [(1,0), (1,2), (0,2), (2,0)]
+         , [(0,1), (2,1), (0,2), (2,0)]
+         , [(0,1), (1,2), (0,2), (2,0)]
+         , [(0,1), (1,2), (2,1), (0,2), (2,0)]
+         , [(0,1), (1,2), (1,2), (2,1), (0,2), (2,0)]
+         ]
+
+make :: [(Int, Int)] -> LGraph D () ()
+make xs = mkGraph (replicate (length xs) ()) $ zip xs $ repeat ()
diff --git a/src/IGraph/Mutable.hs b/src/IGraph/Mutable.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Mutable.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module IGraph.Mutable where
+
+import Foreign
+import Control.Monad.Primitive
+
+import IGraph.Internal.Graph
+import IGraph.Internal.Selector
+import IGraph.Internal.Data
+import IGraph.Internal.Attribute
+import IGraph.Internal.Initialization
+
+-- constants
+vertexAttr :: String
+vertexAttr = "vertex_attribute"
+
+edgeAttr :: String
+edgeAttr = "edge_attribute"
+
+type LEdge a = (Int, Int, a)
+
+-- | Mutable labeled graph
+newtype MLGraph m d v e = MLGraph IGraphPtr
+
+class MGraph d where
+    new :: PrimMonad m => Int -> m (MLGraph (PrimState m) d v e)
+
+    addNodes :: PrimMonad m => Int -> MLGraph(PrimState m) d v e -> m ()
+    addNodes n (MLGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr
+
+    addLNodes :: (Show v, PrimMonad m)
+                 => Int  -- ^ the number of new vertices add to the graph
+                 -> [v]  -- ^ vertices' labels
+                 -> MLGraph (PrimState m) d v e -> m ()
+    addLNodes n labels (MLGraph g)
+        | n /= length labels = error "addLVertices: incorrect number of labels"
+        | otherwise = unsafePrimToPrim $ do
+            let attr = makeAttributeRecord vertexAttr labels
+            alloca $ \ptr -> do
+                poke ptr attr
+                vptr <- listToVectorP [castPtr ptr]
+                withVectorPPtr vptr $ \p -> igraphAddVertices g n $ castPtr p
+
+    delNodes :: PrimMonad m => [Int] -> MLGraph (PrimState m) d v e -> m ()
+    delNodes ns (MLGraph g) = unsafePrimToPrim $ do
+        vptr <- listToVector $ map fromIntegral ns
+        vsptr <- igraphVsVector vptr
+        igraphDeleteVertices g vsptr
+        return ()
+
+    addEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
+
+    addLEdges :: (PrimMonad m, Show e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m ()
+
+    delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
+
+data U = U
+data D = D
+
+instance MGraph U where
+    new n = unsafePrimToPrim $ igraphInit >>= igraphNew n False >>= return . MLGraph
+
+    addEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vec <- listToVector xs
+        igraphAddEdges g vec nullPtr
+      where
+        xs = concatMap ( \(a,b) -> [fromIntegral a, fromIntegral b] ) es
+
+    addLEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vec <- listToVector $ concat xs
+        let attr = makeAttributeRecord edgeAttr vs
+        alloca $ \ptr -> do
+            poke ptr attr
+            vptr <- listToVectorP [castPtr ptr]
+            withVectorPPtr vptr $ \p -> igraphAddEdges g vec $ castPtr p
+      where
+        (xs, vs) = unzip $ map ( \(a,b,v) -> ([fromIntegral a, fromIntegral b], v) ) es
+
+    delEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vptr <- listToVector $ map fromIntegral eids
+        esptr <- igraphEsVector vptr
+        igraphDeleteEdges g esptr
+        return ()
+      where
+        eids = flip map es $ \(fr, to) -> igraphGetEid g fr to False True
+
+instance MGraph D where
+    new n = unsafePrimToPrim $ igraphInit >>= igraphNew n True >>= return . MLGraph
+
+    addEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vec <- listToVector xs
+        igraphAddEdges g vec nullPtr
+      where
+        xs = concatMap ( \(a,b) -> [fromIntegral a, fromIntegral b] ) es
+
+    addLEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vec <- listToVector $ concat xs
+        let attr = makeAttributeRecord edgeAttr vs
+        alloca $ \ptr -> do
+            poke ptr attr
+            vptr <- listToVectorP [castPtr ptr]
+            withVectorPPtr vptr $ \p -> igraphAddEdges g vec $ castPtr p
+      where
+        (xs, vs) = unzip $ map ( \(a,b,v) -> ([fromIntegral a, fromIntegral b], v) ) es
+
+    delEdges es (MLGraph g) = unsafePrimToPrim $ do
+        vptr <- listToVector $ map fromIntegral eids
+        esptr <- igraphEsVector vptr
+        igraphDeleteEdges g esptr
+        return ()
+      where
+        eids = flip map es $ \(fr, to) -> igraphGetEid g fr to True True
diff --git a/src/IGraph/Read.hs b/src/IGraph/Read.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Read.hs
@@ -0,0 +1,46 @@
+module IGraph.Read
+    ( readAdjMatrix
+    , fromAdjMatrix
+    , readAdjMatrixWeighted
+    ) where
+
+import qualified Data.ByteString.Char8 as B
+import Data.ByteString.Lex.Fractional (readSigned, readExponential)
+import Data.Maybe (fromJust)
+
+import IGraph
+
+readDouble :: B.ByteString -> Double
+readDouble = fst . fromJust . readSigned readExponential
+{-# INLINE readDouble #-}
+
+readAdjMatrix :: Graph d => FilePath -> IO (LGraph d B.ByteString ())
+readAdjMatrix = fmap fromAdjMatrix . B.readFile
+
+fromAdjMatrix :: Graph d => B.ByteString -> LGraph d B.ByteString ()
+fromAdjMatrix bs =
+    let (header:xs) = B.lines bs
+        mat = map (map readDouble . B.words) xs
+        es = fst $ unzip $ filter f $ zip [ (i,j) | i <- [0..nrow-1], j <- [0..nrow-1] ] $ concat mat
+        nrow = length mat
+        ncol = length $ head mat
+    in if nrow /= ncol
+         then error "fromAdjMatrix: nrow != ncol"
+         else mkGraph (B.words header) $ zip es $ repeat ()
+  where
+    f ((i,j),v) = i < j && v /= 0
+{-# INLINE fromAdjMatrix #-}
+
+readAdjMatrixWeighted :: Graph d => FilePath -> IO (LGraph d B.ByteString Double)
+readAdjMatrixWeighted fl = do
+    c <- B.readFile fl
+    let (header:xs) = B.lines c
+        mat = map (map readDouble . B.words) xs
+        (es, ws) = unzip $ filter f $ zip [ (i,j) | i <- [0..nrow-1], j <- [0..nrow-1] ] $ concat mat
+        nrow = length mat
+        ncol = length $ head mat
+    if nrow /= ncol
+       then error "nrow != ncol"
+       else return $ mkGraph (B.words header) $ zip es ws
+  where
+    f ((i,j),v) = i < j && v /= 0
diff --git a/src/IGraph/Structure.hs b/src/IGraph/Structure.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Structure.hs
@@ -0,0 +1,115 @@
+module IGraph.Structure
+    ( inducedSubgraph
+    , closeness
+    , betweenness
+    , eigenvectorCentrality
+    , pagerank
+    , personalizedPagerank
+    ) where
+
+import Control.Monad
+import Foreign
+import Foreign.C.Types
+import System.IO.Unsafe (unsafePerformIO)
+import qualified Data.HashMap.Strict as M
+import Data.Hashable (Hashable)
+
+import IGraph
+import IGraph.Mutable
+import IGraph.Internal.Graph
+import IGraph.Internal.Data
+import IGraph.Internal.Selector
+import IGraph.Internal.Structure
+import IGraph.Internal.Arpack
+import IGraph.Internal.Constants
+import IGraph.Internal.Attribute
+
+inducedSubgraph :: (Hashable v, Eq v, Read v) => LGraph d v e -> [Int] -> LGraph d v e
+inducedSubgraph gr vs = unsafePerformIO $ do
+    vs' <- listToVector $ map fromIntegral vs
+    vsptr <- igraphVsVector vs'
+    mallocForeignPtrBytes 160 >>= \gptr -> withForeignPtr gptr $ \p -> do
+        igraphInducedSubgraph (_graph gr) p vsptr IgraphSubgraphCreateFromScratch
+        let g' = IGraphPtr gptr
+            labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1]
+            nV = igraphVcount g'
+            labels = map (read . igraphCattributeVAS g' vertexAttr) [0 .. nV-1]
+        return $ LGraph g' labToId
+
+-- | closeness centrality
+closeness :: [Int]  -- ^ vertices
+          -> LGraph d v e
+          -> Maybe [Double]  -- ^ optional edge weights
+          -> Neimode
+          -> Bool   -- ^ whether to normalize
+          -> [Double]
+closeness vs gr ws mode normal = unsafePerformIO $ do
+    vs' <- listToVector $ map fromIntegral vs
+    vsptr <- igraphVsVector vs'
+    vptr <- igraphVectorNew 0
+    ws' <- case ws of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+    igraphCloseness (_graph gr) vptr vsptr mode ws' normal
+    vectorPtrToList vptr
+
+-- | betweenness centrality
+betweenness :: [Int]
+            -> LGraph d v e
+            -> Maybe [Double]
+            -> [Double]
+betweenness vs gr ws = unsafePerformIO $ do
+    vs' <- listToVector $ map fromIntegral vs
+    vsptr <- igraphVsVector vs'
+    vptr <- igraphVectorNew 0
+    ws' <- case ws of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+    igraphBetweenness (_graph gr) vptr vsptr True ws' False
+    vectorPtrToList vptr
+
+-- | eigenvector centrality
+eigenvectorCentrality :: LGraph d v e
+                      -> Maybe [Double]
+                      -> [Double]
+eigenvectorCentrality gr ws = unsafePerformIO $ do
+    vptr <- igraphVectorNew 0
+    ws' <- case ws of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+    arparck <- igraphArpackNew
+    igraphEigenvectorCentrality (_graph gr) vptr nullPtr True True ws' arparck
+    vectorPtrToList vptr
+
+-- | Google's PageRank
+pagerank :: Graph d
+         => LGraph d v e
+         -> Maybe [Double]  -- ^ edge weights
+         -> Double  -- ^ damping factor, usually around 0.85
+         -> [Double]
+pagerank gr ws d = unsafePerformIO $ alloca $ \p -> do
+    vptr <- igraphVectorNew 0
+    vsptr <- igraphVsAll
+    ws' <- case ws of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+    igraphPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
+        (isDirected gr) d ws' nullPtr
+    vectorPtrToList vptr
+
+personalizedPagerank :: Graph d
+                     => LGraph d v e
+                     -> [Double]   -- ^ reset probability
+                     -> Maybe [Double]
+                     -> Double
+                     -> [Double]
+personalizedPagerank gr reset ws d = unsafePerformIO $ alloca $ \p -> do
+    vptr <- igraphVectorNew 0
+    vsptr <- igraphVsAll
+    ws' <- case ws of
+        Just w -> listToVector w
+        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+    reset' <- listToVector reset
+    igraphPersonalizedPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
+        (isDirected gr) d reset' ws' nullPtr
+    vectorPtrToList vptr
diff --git a/tests/Test/Basic.hs b/tests/Test/Basic.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Basic.hs
@@ -0,0 +1,42 @@
+module Test.Basic
+    ( tests
+    ) where
+
+import Control.Monad.ST
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Utils
+import System.IO.Unsafe
+import Data.List
+
+import IGraph
+import IGraph.Mutable
+import IGraph.Structure
+
+tests :: TestTree
+tests = testGroup "Basic tests"
+    [ graphCreation
+    , graphEdit
+    ]
+
+graphCreation :: TestTree
+graphCreation = testGroup "Graph creation"
+    [ testCase "" $ assertBool "" $ nNodes simple == 3 && nEdges simple == 3
+    , testCase "" $ [(0,1),(1,2),(2,0)] @=? (sort $ edges simple)
+    , testCase "" $ assertBool "" $ nNodes gr == 100 && nEdges gr == 1000
+    , testCase "" $ edgeList @=? (sort $ edges gr)
+    ]
+  where
+    edgeList = sort $ unsafePerformIO $ randEdges 1000 100
+    gr = mkGraph (100,Nothing) (edgeList, Nothing) :: LGraph D () ()
+    simple = mkGraph (3,Nothing) ([(0,1),(1,2),(2,0)],Nothing) :: LGraph D () ()
+
+graphEdit :: TestTree
+graphEdit = testGroup "Graph editing"
+    [ testCase "" $ [(1,2)] @=? (sort $ edges simple') ]
+  where
+    simple = mkGraph (3,Nothing) ([(0,1),(1,2),(2,0)],Nothing) :: LGraph U () ()
+    simple' = runST $ do
+        g <- thaw simple
+        delEdges [(0,1),(0,2)] g
+        freeze g
diff --git a/tests/Test/Structure.hs b/tests/Test/Structure.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Structure.hs
@@ -0,0 +1,33 @@
+module Test.Structure
+    ( tests
+    ) where
+
+import Control.Arrow
+import Control.Monad.ST
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Utils
+import System.IO.Unsafe
+import Data.List
+
+import IGraph
+import IGraph.Mutable
+import IGraph.Structure
+
+tests :: TestTree
+tests = testGroup "Structure property tests"
+    [ subGraphs
+    ]
+
+subGraphs :: TestTree
+subGraphs = testGroup "generate induced subgraphs"
+    [ testCase "" $ test case1 ]
+  where
+    case1 = ( [("a","b"), ("b","c"), ("c","a"), ("a","c")]
+            , ["a","c"], [("a","c"), ("c","a")] )
+    test (ori,ns,expect) = sort expect @=? sort result
+      where
+        gr = fromLabeledEdges ori :: LGraph D String ()
+        ns' = map (head . getNodes gr) ns
+        gr' = inducedSubgraph gr ns'
+        result = map (nodeLab gr' *** nodeLab gr') $ edges gr'
diff --git a/tests/Test/Utils.hs b/tests/Test/Utils.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Utils.hs
@@ -0,0 +1,13 @@
+module Test.Utils where
+
+import Control.Monad
+import System.Random
+import Data.List
+
+randEdges :: Int  -- ^ number of edges to generate
+          -> Int  -- ^ number of nodes in the graph
+          -> IO [(Int, Int)]
+randEdges n nd = do
+    fr <- replicateM (2*n) $ randomRIO (0,nd-1)
+    to <- replicateM (2*n) $ randomRIO (0,nd-1)
+    return $ take n $ nub $ filter (uncurry (/=)) $ zip fr to
diff --git a/tests/test.hs b/tests/test.hs
new file mode 100644
--- /dev/null
+++ b/tests/test.hs
@@ -0,0 +1,9 @@
+import qualified Test.Basic as Basic
+import qualified Test.Structure as Structure
+import Test.Tasty
+
+main :: IO ()
+main = defaultMain $ testGroup "Haskell-igraph Tests"
+    [ Basic.tests
+    , Structure.tests
+    ]
