packages feed

haskell-igraph-0.7.0: src/IGraph/Internal.chs

{-# LANGUAGE ForeignFunctionInterface #-}
module IGraph.Internal
    ( -- * Data structure library: vector, matrix, other data types
      -- ** Igraph vector type and basic operations
      Vector
    , allocaVector
    , allocaVectorN
    , withList
    , withListMaybe
    , toList
    , igraphVectorNull
    , igraphVectorFill
    , igraphVectorE
    , igraphVectorSet
    , igraphVectorTail
    , igraphVectorSize
    , igraphVectorCopyTo

    -- ** Igraph pointer vector
    , VectorPtr
    , allocaVectorPtr
    , allocaVectorPtrN
    , withPtrs
    , toLists
    , igraphVectorPtrSize
    , igraphVectorPtrE
    , igraphVectorPtrSet

      -- ** Customized bytestring for storing attributes
    , BSLen
    , withByteString
    , toByteString

      -- ** Customized bytestring vector
    , BSVector
    , allocaBSVectorN
    , withByteStrings
    , bsvectorSet

      -- ** Igraph matrix type
    , Matrix
    , allocaMatrix
    , allocaMatrixN
    , withRowLists
    , toRowLists
    , toColumnLists
    , igraphMatrixNull
    , igraphMatrixFill
    , igraphMatrixE
    , igraphMatrixSet
    , igraphMatrixCopyTo
    , igraphMatrixNrow
    , igraphMatrixNcol

      -- * Igraph type and constructors
    , IGraph
    , withIGraph
    , allocaIGraph
    , addIGraphFinalizer
    , mkLabelToId
    , initializeNullAttribute
    , igraphNew
    , igraphCreate
    , igraphIsSimple
    , igraphHasMultiple

      -- * Selector and iterator for edge and vertex
      -- ** Igraph vertex selector
    , VertexSelector
    , withVerticesAll
    , withVerticesAdj
    , withVerticesVector
    , withVerticesList

      -- ** Igraph vertex iterator
    , VertexIterator
    , iterateVertices
    , iterateVerticesC

      -- ** Igraph edge Selector
    , EdgeSelector
    , withEdgesAll
    , withEdgeIdsVector
    , withEdgeIdsList

      -- ** Igraph edge iterator
    , EdgeIterator
    , iterateEdges
    , iterateEdgesC

      -- * Basic graph operations
    , igraphCopy
    , igraphVcount
    , igraphEcount
    , igraphGetEid
    , igraphEdge
    , igraphAddVertices
    , igraphAddEdge
    , igraphAddEdges
    , igraphDeleteVertices
    , igraphDeleteEdges

      -- * Igraph attribute record
    , AttributeRecord
    , withAttr
    , withBSAttr
    , igraphHaskellAttributeHasAttr
    , igraphHaskellAttributeVAS
    , igraphHaskellAttributeEAS
    , igraphHaskellAttributeVASSet
    , igraphHaskellAttributeVASSetv
    , igraphHaskellAttributeEASSet
    , igraphHaskellAttributeEASSetv

      -- * Igraph arpack options type
    , ArpackOpt
    , allocaArpackOpt
    ) where

import Control.Monad
import qualified Data.ByteString.Char8 as B
import Data.ByteString (packCStringLen)
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Data.List (transpose)
import qualified Data.Map.Strict as M
import           System.IO.Unsafe          (unsafePerformIO)
import Data.Either (fromRight)
import Data.List.Split (chunksOf)
import Data.Serialize (Serialize, decode, encode)
import           Control.Monad.Primitive
import Control.Exception (bracket_)
import Conduit (ConduitT, yield, liftIO)

import Foreign
import Foreign.C.Types
import Foreign.C.String
import IGraph.Internal.C2HS

{#import IGraph.Internal.Initialization #}
{#import IGraph.Internal.Constants #}
import IGraph.Types

#include "haskell_attributes.h"
#include "haskell_igraph.h"

--------------------------------------------------------------------------------
-- Igraph vector
--------------------------------------------------------------------------------

data Vector

-- | Allocate and initialize a vector.
allocaVector :: (Ptr Vector -> IO a) -> IO a
allocaVector fun = allocaBytes {# sizeof igraph_vector_t #} $ \vec ->
    bracket_ (igraphVectorInit vec 0) (igraphVectorDestroy vec) (fun vec)
{-# INLINE allocaVector #-}

allocaVectorN :: Int -> (Ptr Vector -> IO a) -> IO a
allocaVectorN n fun = allocaBytes {# sizeof igraph_vector_t #} $ \vec ->
    bracket_ (igraphVectorInit vec n) (igraphVectorDestroy vec) (fun vec)
{-# INLINE allocaVectorN #-}

{#fun igraph_vector_init as ^ { castPtr `Ptr Vector', `Int' } -> `CInt' void- #}
{#fun igraph_vector_destroy as ^ { castPtr `Ptr Vector' } -> `CInt' void- #}

withList :: Real a => [a] -> (Ptr Vector -> IO b) -> IO b
withList xs fun = withArrayLen (map realToFrac xs) $ \n ptr ->
    allocaBytes {# sizeof igraph_vector_t #} $ \vec ->
        bracket_ (igraphVectorInitCopy vec ptr n) (igraphVectorDestroy vec) (fun vec)
{-# INLINE withList #-}
{#fun igraph_vector_init_copy as ^
    { castPtr `Ptr Vector'
    , id `Ptr CDouble', `Int' } -> `CInt' void- #}

-- | Allocate a nullPtr if Nothing
withListMaybe :: Real a => Maybe [a] -> (Ptr Vector -> IO b) -> IO b
withListMaybe (Just xs) fun = withList xs fun
withListMaybe Nothing fun = fun $ castPtr nullPtr
{-# INLINE withListMaybe #-}


toList :: Ptr Vector -> IO [Double]
toList vec = do
    n <- igraphVectorSize vec
    allocaArray n $ \ptr -> do
        igraphVectorCopyTo vec ptr
        map realToFrac <$> peekArray n ptr
{-# INLINE toList #-}

{#fun igraph_vector_copy_to as ^ { castPtr `Ptr Vector', id `Ptr CDouble' } -> `()' #}

-- Initializing elements

{#fun igraph_vector_null as ^ { castPtr `Ptr Vector' } -> `()' #}

{#fun igraph_vector_fill as ^ { castPtr `Ptr Vector', `Double' } -> `()' #}


-- Accessing elements

{#fun igraph_vector_e as ^ { castPtr `Ptr Vector', `Int' } -> `Double' #}

{#fun igraph_vector_set as ^ { castPtr `Ptr Vector', `Int', `Double' } -> `()' #}

{#fun igraph_vector_tail as ^ { castPtr `Ptr Vector' } -> `Double' #}


-- Vector properties
{#fun igraph_vector_size as ^ { castPtr `Ptr Vector' } -> `Int' #}


--------------------------------------------------------------------------------
-- Pointer Vector
--------------------------------------------------------------------------------

data VectorPtr

-- | Allocate and initialize a pointer vector.
allocaVectorPtr :: (Ptr VectorPtr -> IO a) -> IO a
allocaVectorPtr fun = allocaBytes {# sizeof igraph_vector_ptr_t #} $ \ptr ->
    bracket_ (igraphVectorPtrInit ptr 0) (igraphVectorPtrDestroy ptr) (fun ptr)
{-# INLINE allocaVectorPtr #-}

allocaVectorPtrN :: Int -> (Ptr VectorPtr -> IO a) -> IO a
allocaVectorPtrN n fun = allocaBytes {# sizeof igraph_vector_ptr_t #} $ \ptr ->
    bracket_ (igraphVectorPtrInit ptr n) (igraphVectorPtrDestroy ptr) (fun ptr)
{-# INLINE allocaVectorPtrN #-}

{#fun igraph_vector_ptr_init as ^ { castPtr `Ptr VectorPtr', `Int' } -> `CInt' void- #}
{#fun igraph_vector_ptr_destroy as ^ { castPtr `Ptr VectorPtr' } -> `()' #}

withPtrs :: [Ptr a] -> (Ptr VectorPtr -> IO b) -> IO b
withPtrs xs fun = allocaVectorPtrN n $ \vptr -> do
    sequence_ $ zipWith (igraphVectorPtrSet vptr) [0..] $ map castPtr xs
    fun vptr
  where
    n = length xs
{-# INLINE withPtrs #-}

toLists :: Ptr VectorPtr -> IO [[Double]]
toLists vptr = do
    n <- igraphVectorPtrSize vptr
    forM [0..n-1] $ \i -> igraphVectorPtrE vptr i >>= toList . castPtr
{-# INLINE toLists #-}

{#fun igraph_vector_ptr_e as ^ { castPtr `Ptr VectorPtr', `Int' } -> `Ptr ()' #}
{#fun igraph_vector_ptr_set as ^ { castPtr `Ptr VectorPtr', `Int', id `Ptr ()' } -> `()' #}
{#fun igraph_vector_ptr_size as ^ { castPtr `Ptr VectorPtr' } -> `Int' #}


--------------------------------------------------------------------------------
-- Customized string vector
--------------------------------------------------------------------------------

data BSLen

toByteString :: Ptr BSLen -> IO B.ByteString
toByteString ptr = do
    n <- {#get bytestring_t->len #} ptr
    str <- {#get bytestring_t->value #} ptr
    packCStringLen (str, fromIntegral n)
{-# INLINE toByteString #-}

withByteString :: B.ByteString -> (Ptr BSLen -> IO a) -> IO a
withByteString x f = unsafeUseAsCStringLen x $ \(str, n) ->
    allocaBytes {#sizeof bytestring_t #} $ \ptr -> do
        {#set bytestring_t.len #} ptr (fromIntegral n)
        {#set bytestring_t.value #} ptr str
        f ptr
{-# INLINE withByteString #-}

data BSVector

allocaBSVectorN :: Int -> (Ptr BSVector -> IO a) -> IO a
allocaBSVectorN n fun = allocaBytes {# sizeof bsvector_t #} $ \ptr ->
    bracket_ (bsvectorInit ptr n) (bsvectorDestroy ptr) (fun ptr)
{-# INLINE allocaBSVectorN #-}

{#fun bsvector_init as ^ { castPtr `Ptr BSVector', `Int' } -> `CInt' void- #}
{#fun bsvector_destroy as ^ { castPtr `Ptr BSVector' } -> `()' #}

withByteStrings :: [B.ByteString] -> (Ptr BSVector -> IO a) -> IO a
withByteStrings xs fun = allocaBSVectorN n $ \bsvec -> do
    foldM_ (\i x -> bsvectorSet bsvec i x >> return (i+1)) 0 xs
    fun bsvec
  where
    n = length xs
{-# INLINE withByteStrings #-}

bsvectorSet :: Ptr BSVector -> Int -> B.ByteString -> IO ()
bsvectorSet vec i bs = withByteString bs (bsvectorSet' vec i)
{-# INLINE bsvectorSet #-}
{#fun bsvector_set as bsvectorSet'
    { castPtr `Ptr BSVector', `Int', castPtr `Ptr BSLen' } -> `()' #}


--------------------------------------------------------------------------------
-- Matrix
--------------------------------------------------------------------------------

data Matrix

allocaMatrix :: (Ptr Matrix -> IO a) -> IO a
allocaMatrix fun = allocaBytes {# sizeof igraph_matrix_t #} $ \mat ->
    bracket_ (igraphMatrixInit mat 0 0) (igraphMatrixDestroy mat) (fun mat)
{-# INLINE allocaMatrix #-}

allocaMatrixN :: Int   -- ^ Number of rows
              -> Int   -- ^ Number of columns
              -> (Ptr Matrix -> IO a) -> IO a
allocaMatrixN r c fun = allocaBytes {# sizeof igraph_matrix_t #} $ \mat ->
    bracket_ (igraphMatrixInit mat r c) (igraphMatrixDestroy mat) (fun mat)
{-# INLINE allocaMatrixN #-}

{#fun igraph_matrix_init as ^ { castPtr `Ptr Matrix', `Int', `Int' } -> `CInt' void- #}
{#fun igraph_matrix_destroy as ^ { castPtr `Ptr Matrix' } -> `()' #}

-- row lists to matrix
withRowLists :: Real a => [[a]] -> (Ptr Matrix -> IO b) -> IO b
withRowLists xs fun
    | all (==c) $ map length xs = allocaMatrixN r c $ \mat -> do
        forM_ (zip [0..] xs) $ \(i, row) ->
            forM_ (zip [0..] row) $ \(j,v) ->
                igraphMatrixSet mat i j $ realToFrac v
        fun mat
    | otherwise = error "Not a matrix."
  where
    r = length xs
    c = length $ head xs
{-# INLINE withRowLists #-}

-- to row lists
toRowLists :: Ptr Matrix -> IO [[Double]]
toRowLists = fmap transpose . toColumnLists

toColumnLists :: Ptr Matrix -> IO [[Double]]
toColumnLists 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

{#fun igraph_matrix_null as ^ { castPtr `Ptr Matrix' } -> `()' #}

{#fun igraph_matrix_fill as ^ { castPtr `Ptr Matrix', `Double' } -> `()' #}

{#fun igraph_matrix_e as ^ { castPtr `Ptr Matrix', `Int', `Int' } -> `Double' #}

{#fun igraph_matrix_set as ^ { castPtr `Ptr Matrix', `Int', `Int', `Double' } -> `()' #}

{#fun igraph_matrix_copy_to as ^ { castPtr `Ptr Matrix', id `Ptr CDouble' } -> `()' #}

{#fun igraph_matrix_nrow as ^ { castPtr `Ptr Matrix' } -> `Int' #}

{#fun igraph_matrix_ncol as ^ { castPtr `Ptr Matrix' } -> `Int' #}


--------------------------------------------------------------------------------
-- Graph Constructors and Destructors
--------------------------------------------------------------------------------

{#pointer *igraph_t as IGraph foreign finalizer igraph_destroy newtype#}

allocaIGraph :: (Ptr IGraph -> IO a) -> IO a
allocaIGraph f = mallocBytes {# sizeof igraph_t #} >>= f
{-# INLINE allocaIGraph #-}

mkLabelToId :: (Ord v, Serialize v) => IGraph -> M.Map v [Int]
mkLabelToId gr = unsafePerformIO $ do
    n <- igraphVcount gr
    fmap (M.fromListWith (++)) $ forM [0..n-1] $ \i -> do
        l <- igraphHaskellAttributeVAS gr vertexAttr i >>= toByteString >>=
            return . fromRight (error "decode failed") . decode
        return (l, [i])
{-# INLINE mkLabelToId #-}

initializeNullAttribute :: PrimMonad m
                        => IGraph
                        -> m ()
initializeNullAttribute gr = unsafePrimToPrim $ do
    nn <- igraphVcount gr
    unsafePrimToPrim $ withByteStrings (map encode $ replicate nn ()) $
        igraphHaskellAttributeVASSetv gr vertexAttr
    ne <- igraphEcount gr
    unsafePrimToPrim $ withByteStrings (map encode $ replicate ne ()) $
        igraphHaskellAttributeEASSetv gr edgeAttr
{-# INLINE initializeNullAttribute #-}

addIGraphFinalizer :: Ptr IGraph -> IO IGraph
addIGraphFinalizer ptr = do
    vec <- newForeignPtr igraph_destroy ptr
    return $ IGraph vec
{-# INLINE addIGraphFinalizer #-}

-- | Create a igraph object and attach a finalizer
igraphNew :: Int -> Bool -> HasInit -> IO IGraph
igraphNew n directed _ = igraphNew' n directed
{#fun igraph_empty as igraphNew'
    { allocaIGraph- `IGraph' addIGraphFinalizer*
    , `Int', `Bool'
    } -> `CInt' void- #}

{#fun igraph_copy as ^
    { allocaIGraph- `IGraph' addIGraphFinalizer*
    , `IGraph'
    } -> `CInt' void- #}

{#fun igraph_create as ^
    { allocaIGraph- `IGraph' addIGraphFinalizer*
    , castPtr `Ptr Vector'  -- ^ The edges to add, the first two elements are
                            -- the first edge, etc.
    , `Int'    -- ^ The number of vertices in the graph, if smaller or equal to
               -- the highest vertex id in the edges vector it will be
               -- increased automatically. So it is safe to give 0 here.
    , `Bool'   -- ^ Whether to create a directed graph or not. If yes,
               -- then the first edge points from the first vertex id in edges
               -- to the second, etc.
    } -> `CInt' void- #}

-- | A graph is a simple graph if it does not contain loop edges and multiple edges.
{#fun igraph_is_simple as ^
    { `IGraph'
    , alloca- `Bool' peekBool*
    } -> `CInt' void- #}

{#fun igraph_has_multiple as ^
    { `IGraph'
    , alloca- `Bool' peekBool*
    } -> `CInt' void- #}

{#fun igraph_to_directed as ^
    { `IGraph'   -- ^ The graph object to convert.
    , `ToDirected'   -- ^ Specifies the details of how exactly the conversion is
                     -- done. Possible values: IGRAPH_TO_DIRECTED_ARBITRARY:
                     -- the number of edges in the graph stays the same,
                     -- an arbitrarily directed edge is created for each
                     -- undirected edge; IGRAPH_TO_DIRECTED_MUTUAL: two directed
                     -- edges are created for each undirected edge, one in each direction.
    } -> `CInt' void- #}


--------------------------------------------------------------------------------
-- Vertex selector
--------------------------------------------------------------------------------

data VertexSelector

allocaVertexSelector :: (Ptr VertexSelector -> IO a) -> IO a
allocaVertexSelector fun = allocaBytes {# sizeof igraph_vs_t #} $ \vs -> do
    r <- fun vs
    igraphVsDestroy vs
    return r
{-# INLINE allocaVertexSelector #-}

{#fun igraph_vs_destroy as ^ { castPtr `Ptr VertexSelector' } -> `()' #}

withVerticesAll :: (Ptr VertexSelector -> IO a) -> IO a
withVerticesAll fun = allocaVertexSelector $ \vs -> igraphVsAll vs >> fun vs
{-# INLINE withVerticesAll #-}
{#fun igraph_vs_all as ^ { castPtr `Ptr VertexSelector' } -> `CInt' void- #}

withVerticesAdj :: Int -> Neimode -> (Ptr VertexSelector -> IO a) -> IO a
withVerticesAdj i mode fun = allocaVertexSelector $ \vs -> igraphVsAdj vs i mode >> fun vs
{-# INLINE withVerticesAdj #-}
{#fun igraph_vs_adj as ^
    { castPtr `Ptr VertexSelector', `Int', `Neimode' } -> `CInt' void- #}

withVerticesVector :: Ptr Vector -> (Ptr VertexSelector -> IO a) -> IO a
withVerticesVector vec fun = allocaVertexSelector $ \vs -> igraphVsVector vs vec >> fun vs
{-# INLINE withVerticesVector #-}
{#fun igraph_vs_vector as ^
    { castPtr `Ptr VertexSelector', castPtr `Ptr Vector' } -> `CInt' void- #}

withVerticesList :: Real a => [a] -> (Ptr VertexSelector -> IO b) -> IO b
withVerticesList xs fun = withList xs $ \vec -> withVerticesVector vec fun
{-# INLINE withVerticesList #-}


--------------------------------------------------------------------------------
-- Vertex iterator
--------------------------------------------------------------------------------

data VertexIterator

iterateVertices :: IGraph -> Ptr VertexSelector -> (Ptr VertexIterator -> IO a) -> IO a
iterateVertices gr vs fun = allocaBytes {# sizeof igraph_vit_t #} $ \vit ->
    bracket_ (igraphVitCreate gr vs vit) (igraphVitDestroy vit) (fun vit)
{-# INLINE iterateVertices #-}

iterateVerticesC :: IGraph
                 -> Ptr VertexSelector
                 -> (ConduitT i Int IO () -> IO a)
                 -> IO a
iterateVerticesC gr vs fun = allocaBytes {# sizeof igraph_vit_t #} $ \vit ->
    bracket_ (igraphVitCreate gr vs vit) (igraphVitDestroy vit) (fun $ sourceVertexIterator vit)
{-# INLINE iterateVerticesC #-}

{#fun igraph_vit_create as ^
    { `IGraph'
    , castPtr %`Ptr VertexSelector'
    , castPtr `Ptr VertexIterator'
    } -> `CInt' void- #}
{#fun igraph_vit_destroy as ^ { castPtr `Ptr VertexIterator' } -> `()' #}


sourceVertexIterator :: Ptr VertexIterator -> ConduitT i Int IO ()
sourceVertexIterator vit = do
    isEnd <- liftIO $ igraphVitEnd vit
    if isEnd
      then return ()
      else do
        liftIO (igraphVitGet vit) >>= yield
        liftIO $ igraphVitNext vit
        sourceVertexIterator vit
{-# INLINE sourceVertexIterator #-}

#c
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_end as ^ { castPtr `Ptr VertexIterator' } -> `Bool' #}
{#fun igraph_vit_next as ^ { castPtr `Ptr VertexIterator' } -> `()' #}
{#fun igraph_vit_get as ^ { castPtr `Ptr VertexIterator' } -> `Int' #}


--------------------------------------------------------------------------------
-- Edge Selector
--------------------------------------------------------------------------------

data EdgeSelector

allocaEdgeSelector :: (Ptr EdgeSelector -> IO a) -> IO a
allocaEdgeSelector fun = allocaBytes {# sizeof igraph_es_t #} $ \es -> do
    r <- fun es
    igraphEsDestroy es
    return r
{-# INLINE allocaEdgeSelector #-}
{#fun igraph_es_destroy as ^ { castPtr `Ptr EdgeSelector' } -> `()' #}

withEdgesAll :: EdgeOrderType -> (Ptr EdgeSelector -> IO a) -> IO a
withEdgesAll ord fun = allocaEdgeSelector $ \es -> igraphEsAll es ord >> fun es
{-# INLINE withEdgesAll #-}
{#fun igraph_es_all as ^ { castPtr `Ptr EdgeSelector', `EdgeOrderType'} -> `CInt' void- #}

withEdgeIdsVector :: Ptr Vector -> (Ptr EdgeSelector -> IO a) -> IO a
withEdgeIdsVector vec fun = allocaEdgeSelector $ \es ->
    igraphEsVector es vec >> fun es
{-# INLINE withEdgeIdsVector #-}
{# fun igraph_es_vector as ^
    { castPtr `Ptr EdgeSelector', castPtr `Ptr Vector' } -> `CInt' void- #}

withEdgeIdsList :: [Int] -> (Ptr EdgeSelector -> IO b) -> IO b
withEdgeIdsList xs fun = withList xs $ \vec -> withEdgeIdsVector vec fun
{-# INLINE withEdgeIdsList #-}


--------------------------------------------------------------------------------
-- Edge iterator
--------------------------------------------------------------------------------

data EdgeIterator

iterateEdges :: IGraph -> Ptr EdgeSelector -> (Ptr EdgeIterator -> IO a) -> IO a
iterateEdges gr es fun = allocaBytes {# sizeof igraph_eit_t #} $ \eit ->
    bracket_ (igraphEitCreate gr es eit) (igraphEitDestroy eit) (fun eit)
{-# INLINE iterateEdges #-}
{#fun igraph_eit_create as ^ { `IGraph', castPtr %`Ptr EdgeSelector', castPtr `Ptr EdgeIterator' } -> `CInt' void- #}
{#fun igraph_eit_destroy as ^ { castPtr `Ptr EdgeIterator' } -> `()' #}

iterateEdgesC :: IGraph
              -> Ptr EdgeSelector
              -> (ConduitT i Int IO () -> IO a)
              -> IO a
iterateEdgesC gr es fun = allocaBytes {# sizeof igraph_eit_t #} $ \eit ->
    bracket_ (igraphEitCreate gr es eit) (igraphEitDestroy eit) (fun $ sourceEdgeIterator eit)
{-# INLINE iterateEdgesC #-}

sourceEdgeIterator :: Ptr EdgeIterator -> ConduitT i Int IO ()
sourceEdgeIterator eit = do
    isEnd <- liftIO $ igraphEitEnd eit
    if isEnd
      then return ()
      else do
        liftIO (igraphEitGet eit) >>= yield
        liftIO $ igraphEitNext eit
        sourceEdgeIterator eit
{-# INLINE sourceEdgeIterator #-}

#c
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_end as ^ { castPtr `Ptr EdgeIterator' } -> `Bool' #}
{#fun igraph_eit_next as ^ { castPtr `Ptr EdgeIterator' } -> `()' #}
{#fun igraph_eit_get as ^ { castPtr `Ptr EdgeIterator' } -> `Int' #}


--------------------------------------------------------------------------------
-- Basic Query Operations
--------------------------------------------------------------------------------

{#fun igraph_vcount as ^ { `IGraph' } -> `Int' #}

{#fun igraph_ecount as ^ { `IGraph' } -> `Int' #}

{#fun igraph_get_eid as ^
    { `IGraph'
    , alloca- `Int' peekIntConv*
    , `Int'
    , `Int'
    , `Bool'
    , `Bool'
    } -> `CInt' void-#}

{#fun igraph_edge as ^
    { `IGraph'
    , `Int'
    , alloca- `Int' peekIntConv*
    , alloca- `Int' peekIntConv*
    } -> `CInt' void-#}

-- Adding and Deleting Vertices and Edges

{# fun igraph_add_vertices as ^ { `IGraph', `Int', id `Ptr ()' } -> `()' #}

{# fun igraph_add_edge as ^ { `IGraph', `Int', `Int' } -> `()' #}

-- | The edges are given in a vector, the first two elements define the first
-- edge (the order is from , to for directed graphs). The vector should
-- contain even number of integer numbers between zero and the number of
-- vertices in the graph minus one (inclusive). If you also want to add
-- new vertices, call igraph_add_vertices() first.
{# fun igraph_add_edges as ^
    { `IGraph'     -- ^ The graph to which the edges will be added.
    , castPtr `Ptr Vector'     -- ^ The edges themselves.
    , id `Ptr ()'  -- ^ The attributes of the new edges.
    } -> `()' #}

-- | delete vertices
{# fun igraph_delete_vertices as ^
    { `IGraph', castPtr %`Ptr VertexSelector' } -> `CInt' void- #}

-- | delete edges
{# fun igraph_delete_edges as ^
    { `IGraph', castPtr %`Ptr EdgeSelector' } -> `CInt' void- #}

data AttributeRecord

withAttr :: Serialize a
         => String   -- ^ Attribute name
         -> [a]      -- ^ Attributes
         -> (Ptr AttributeRecord -> IO b) -> IO b
withAttr name xs fun = withByteStrings (map encode xs) $ \bsvec ->
    withBSAttr name bsvec fun
{-# INLINE withAttr #-}

withBSAttr :: String          -- ^ Attribute name
           -> Ptr BSVector    -- ^ Attributes
           -> (Ptr AttributeRecord -> IO b) -> IO b
withBSAttr name bsvec fun = withCString name $ \name' ->
    allocaBytes {#sizeof igraph_attribute_record_t #} $ \attr ->
        setAttribute attr name' (castPtr bsvec) >> fun attr
  where
    setAttribute attr x y = do
        {#set igraph_attribute_record_t.name #} attr x
        {#set igraph_attribute_record_t.type #} attr 2
        {#set igraph_attribute_record_t.value #} attr y
{-# INLINE withBSAttr #-}

-- | Checks whether a (graph, vertex or edge) attribute exists
{#fun igraph_haskell_attribute_has_attr as ^
    { `IGraph'
    , `AttributeElemtype' -- ^ The type of the attribute
    , `String' -- ^ The name of the attribute
    } -> `Bool' #}

-- | Query a string vertex attribute
{#fun igraph_haskell_attribute_VAS as ^
    { `IGraph'
    , `String'    -- ^ The name of the attribute
    , `Int'       -- ^ The id of the queried vertex
    } -> `Ptr BSLen' castPtr #}

-- | Query a string edge attribute.
{#fun igraph_haskell_attribute_EAS as ^
    { `IGraph'
    , `String'  -- ^ The name of the attribute
    , `Int'     -- ^ The id of the queried edge
    } -> `Ptr BSLen' castPtr #}

{#fun igraph_haskell_attribute_VAS_set as ^
    { `IGraph'
    , `String'
    , `Int'
    , castPtr `Ptr BSLen'
    } -> `CInt' void-#}

{#fun igraph_haskell_attribute_VAS_setv as ^
    { `IGraph'
    , `String'   -- ^ Name of the attribute
    , castPtr `Ptr BSVector'   -- ^ String vector, the new attribute values.
                               -- The length of this vector must match the
                               -- number of vertices.
    } -> `CInt' void-#}

-- | Set a string edge attribute.
{#fun igraph_haskell_attribute_EAS_set as ^
    { `IGraph'
    , `String'  -- ^ The name of the attribute
    , `Int'     -- ^ The id of the queried vertex
    , castPtr `Ptr BSLen'   -- ^ The (new) value of the attribute.
    } -> `CInt' void-#}

-- | Set a string edge attribute for all edges.
{#fun igraph_haskell_attribute_EAS_setv as ^
    { `IGraph'
    , `String'   -- ^ Name of the attribute
    , castPtr `Ptr BSVector'   -- ^ String vector, the new attribute values.
                               -- The length of this vector must match the
                               -- number of edges.
    } -> `CInt' void-#}


--------------------------------------------------------------------------------
-- Arpack options
--------------------------------------------------------------------------------

data ArpackOpt

allocaArpackOpt :: (Ptr ArpackOpt -> IO a) -> IO a
allocaArpackOpt fun = allocaBytes {# sizeof igraph_arpack_options_t #} $ \opt -> do
    igraphArpackOptionsInit opt >> fun opt
{-# INLINE allocaArpackOpt #-}
{#fun igraph_arpack_options_init as ^ { castPtr `Ptr ArpackOpt' } -> `CInt' void- #}